博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
003. 如何将老项目的小程序快速改为分包模式
阅读量:5809 次
发布时间:2019-06-18

本文共 8262 字,大约阅读时间需要 27 分钟。

背景

在最开始准备制作小程序的时候,为了求开发的快速,所使用了直接page的引入方式,每次小程序加载都是全包下载。3月份之前小程序页面稳定在35个,下载耗时在1800ms左右,4月份初上线了新业务页面膨胀到52个页面时,下载耗时基本稳定在2300ms左右,随着用户反馈越来越多,所以开始准备小程序采用分包加载,来解决这个问题。5月份在分包加载之后,页面依旧稳定在52个,但是下载耗时稳定800ms左右,缩短了将近1500ms。基本做到了0业务入侵、加入npm script 来实现新建页面,同时兼容分包加载,page加载。

操作流程

执行命令

npm run new复制代码

给出信息提示,并且要求输入路径

[Info] 帮助你快速创建page文件[Info] 只局限于新目录创建[Info] 请输入文件路径,已帮你省略pages/[Info] 例子:address/addressList? 请输入文件路径:复制代码

输入路径之后创建文件的提示

[Info] 已创建base64.js[Info] 已创建index.js[Info] 已创建index.json[Info] 已创建index.wxml[Info] 已创建index.wxss[Info] 创建完成!!!!复制代码

选择包,是主包、还是分包,并且提示,给出提示,如果是tabBar中使用,只能选择主包,并且手动配置app.json的tabBar

? 你想生成什么样的包: (Use arrow keys)❯ 主包,在pages中写入,如果是tabBar中使用,只能选择主包,并且手动配置app.json的tabBar  分包,在subpackages中写入复制代码

单选完成之后提示新建成功

[Info] 写入成功复制代码

第一次分包

首先我们对未分包和分包的app.json代码进行对比

// 未分包"pages": [  "pages/a/index",  "pages/b/index",  "pages/b/list",]复制代码
// 实现分包"subpackages": [  {    "root": "pages/a",    "pages": [      "index",    ]  },  {    "root": "pages/b",    "pages": [      "index",      "list"    ]  }]复制代码

接下来看目录结构

pages  |---- a        |---- index.js  |---- b        |---- index.js        |---- list.jsapp.json复制代码

简单分一下,第一次分包的核心业务是,将pages这个普通列表,变成名为subpackages的二叉树结构,左子节点是root,右子节点是pages,并且右子节点是string类型,左子节点是array类型。好吧,我可以定下规则,首先我可以将pages/a/index这个字符串变为数组以/为分界的['pages','a','index']数组,然后我将前两项作为root节点的参数,用slice操作截取出来,转为字符串类型,赋值给root。之后的所有参数,我都赋值或者push给pages参数,最后我就得到了这样一个符合要求的树,最后push进subpackages即可。至于数据源,我只需要读出app.json这个文件的pages参数,并且通过我的方法写入subpackages参数即可。

要点

  • 去重,因为subpackages内,root节点是唯一的,但是在pages里面可能我会截取初重复的值

解决

  • 使用Map结构,把截取出的root值作为key,剩下的作为value,Map数据这种数据结构的特点就是key值唯一。
let list = [  "pages/a/index",  "pages/a/list",  "pages/a/detail/index",  "pages/c/list",  "pages/b/index",];let m = new Map();let packages = [];list.forEach(v=>{  let arr = v.split('/');  let root = arr.splice(0,2).join('/');  let pages = arr.join('/');  if(m.get(root)){    let s = m.get(root);    m.set(root,[...s, pages]);  }else{    m.set(root,[pages]);  };});for(let [key,value] of m){  packages.push({    root: key,    pages: value,  })}console.log(packages);复制代码
// log 出的结构[  { root: 'pages/a', pages: [ 'index', 'list', 'detail/index' ] },  { root: 'pages/c', pages: [ 'list' ] },  { root: 'pages/b', pages: [ 'index' ] }]复制代码

我已经在不入侵业务的情况下实现了小程序的第一次分包,节约了我手动去改的劳动力,我个人认为,解决问题,上策用数据结构,中策写兼容代码,下策手动去改。至于,读出写入文件,我就不赘述了,google即可。

创建新pages的指令编写

好吧,我实现了第一次的分包。然后我要思考,如果我每次要加页面的话,是不是就要去查看subpackages,找到对应的root,并且添加pages。这么重复劳动力的操作,我为什么不用脚本替代呢,是吧。

核心需求

  • 编写交互式的命令输入
  • 检测输入的page目录是否存在,存在就报错
  • 不存在创建page目录,复制template到新建的page中
  • 根据使用者的单选,选择写入pages,或者写入subpackages
  • 在page文件加中设置预留文件夹,用做初级业务拆分,不加入subpackages检测
  • 屏蔽app.json中pages的文件,不加入subpackages检测

设计npm script

{  "subcontract": "node ./config/subcontract",  "new": "node ./config/new"}  复制代码

添加的package.json参数,ignore-files表示文件夹中不检测的文件,pages表式app.json中的文件

{  "ignore-files": [    "**/common/**",    "**/component/**",    "**/
/**", "**/
/**", "**/
/**", ], "pages": [ "pages/
/index", "pages/
/index", "pages/
/index", ]}复制代码

需求已经明确,我就要去找我需要用到的npm包了

colors        命令行颜色inquirer      交互式命令行glob          全局搜索文件fs-extra      文件写入写出path          路径shelljs       执行shell命令复制代码

分析new.js文件

const colors = require('colors');const inquirer = require('inquirer');const glob = require('glob');const fs = require('fs-extra');const path = require('path');const shell = require('shelljs');const PKG = require('../package.json');const ROOT = path.resolve(__dirname, '../');let appJson = require('../app.json');const promps = [{  type: 'input',  name: 'pagePath',  message: '请输入文件路径:',},{  type: 'list',  name: 'type',  message: '你想生成什么样的包:',  choices: [    {      name: '主包,在pages中写入,如果是tabBar中使用,只能选择主包,并且手动配置app.json的tabBar',      value: '1',    },    {      name: '分包,在subpackages中写入',      value: '2',    },  ],}];const logger = {  info(msg) {    console.log(`[Info] ${colors.green(msg)}`);  },  warn(msg) {    console.log(`[Warn] ${colors.yellow(msg)}`);  },  error(msg) {    console.log(`[Error] ${colors.red(msg)}(/‵Д′)/~ ╧╧`);  },};logger.info('帮助你快速创建page文件');logger.info('只局限于新目录创建');logger.info('请输入文件路径,已帮你省略pages/');logger.info('例子:xxxxx/xxxx');复制代码

这是代码中的常量部分和默认提示部分,我写了logeer对象来作为提示输出的默认颜色,promps作为我交互命令行的基础配置。引入package.json我的主要目的是因为我屏蔽了一些文件ignore-filespages,想这两个参数的文件夹我是不会被检测的,不会被加入到subpackages的。

function checkFile(name) {  const options = {    ignore: [      '**/*.js',      '**/*.wxss',      '**/*.wxml',      '**/*.json',    ],    cwd: 'pages/',  };  const files = glob.sync('**', options);  if (files.some((v) => v === name)) {    logger.error('输入的目录已经存在,已终止!!!!');    return false;  };  return name;};复制代码

在这是检测文件是否存在的方法,只需要传入路径,即可检测这个路径是否在pages/目录中存在。

function buildFile(name) {  const options = {    cwd: 'template/page',  };  const files = glob.sync(`**`, options);  files.forEach((v)=>{    const file = v.split('.tp')[0];    fs.copy(`${ROOT}/template/page/${v}`, `${ROOT}/pages/${name}/${file}`, (err) => {      if (err) {        console.error(err);        return false;      }    });    logger.info(`已创建${file}`);  });  logger.info('创建完成!!!!');  return true;};复制代码

这是复制文件夹并且复制模版文件的方法,我准备了tempalte这个文件夹,用来存储我写的模版文件,创建完成之后,我直接复制进去即可。为了和普通文件区别,我添加的.tp后缀。我的模版是可扩展的,我可以把requestapp({...})在其中写好,并且添加我个人的一些方法。

function subcontract(res) {  inquirer.prompt(promps[1]).then((answers)=>{    if (answers.type === '1') {      PKG['ignore-files'].push(`${res}/**`);      PKG['pages'].push(`${res}/index`);      appJson['pages'].push(`pages/${res}/index`);      fs.writeFileSync(`${ROOT}/app.json`, JSON.stringify(appJson, null, 2));      fs.writeFileSync(`${ROOT}/package.json`, JSON.stringify(PKG, null, 2));      logger.info('写入成功');    };    if (answers.type === '2') shell.exec('npm run subcontract');  });};复制代码

这是选择pages还是subcontract的方法,选择了subcontract,我直接执行我上面写的小程序分包方法subcontract.js即可。如果选择pages,我会将它加入package.json中的pages对象,这个对象表式这些文件名不被subcontract脚本检测。

async function inquirers() {  const {pagePath} = await inquirer.prompt(promps[0]);  const path = pagePath.replace(/\s+/g, '');  if (!path) {    logger.error('输入有失误,已终止!!!!');    return false;  };  if (/.*[\u4e00-\u9fa5]+.*$/.test(path)) {    logger.error('请不要输入中文符号,已终止!!!!');    return false;  };  return path;};复制代码

检测输入值是否合法,是否有中文,并且去除空格

( async function() {  const inquirerRes = await inquirers();  const checkFileRes = inquirerRes && checkFile(inquirerRes);  const buildFileRes = checkFileRes && buildFile(checkFileRes);  buildFileRes && subcontract(checkFileRes);})();复制代码

最后组装,inquirerRes变量负责判断输入值是否正确。然后进入checkFile,来检测文件夹是否重复。调用buildFile方法,创建文件夹,复制模版文件。最后调用subcontract来判断是分包还是主包。

subcontract.js 分析

const glob = require('glob');const fs = require('fs');const path = require('path');const colors = require('colors');const ROOT = path.resolve(__dirname, '../');const PAG = require('../package.json');let appJson = require('../app.json');const ignoreFiles = PAG['ignore-files'];const pages = PAG['pages'];const logger = {  info(msg) {    console.log(`[Info] ${colors.green(msg)}`);  },  warn(msg) {    console.log(`[Warn] ${colors.yellow(msg)}`);  },  error(msg) {    console.log(`[Error] ${colors.red(msg)}(/‵Д′)/~ ╧╧`);  },};const subcontract = () => {  const options = {    ignore: ignoreFiles,    cwd: 'pages/',  };  const files = glob.sync('**/index.js', options);  let subcontractMap = new Map();  files.forEach((v)=>{    let arr = v.split('.')[0].split('/');    let root = arr.shift();    let page = arr.join('/');    if (subcontractMap.has(root)) {      let pages = subcontractMap.get(root);      pages.push(page);      subcontractMap.set(root, pages);    } else {      subcontractMap.set(root, [page]);    }  });  let subcontractList = [];  subcontractMap.forEach((v, k)=>{    subcontractList.push({      root: `pages/${k}`,      pages: v,    });  });  return subcontractList;};appJson.subpackages = subcontract();appJson.pages = pages;fs.writeFileSync(`${ROOT}/app.json`, JSON.stringify(appJson, null, 2));logger.info('写入成功');复制代码

这个方法其实和小程序第一次分包的方法大同小异。只不过我修改了数据源的获取,第一次我是读取app.json的pages,这里我是根据目录来的,以及加入了ignoreFiles来做文件屏蔽。以及一些友好提示。

结尾

项目优化的道路还有很长这只是最最初步的方案。为什么我开始不直接选择分包呢?因为项目开始的时候还没有分包,而且如果有,我感觉分包机制的这种书写方式,可能会带给开发者出错的可能性,我为了将项目工期缩短,出错可能性降低,我也不会选择一开始就分包。当业务增长到一定量,以及业务逐渐趋向于稳定时候,我就可以根据业务的特性,去做相对应的事情。这种方式我称之为技术迭代。在特定的时候,选择特定的解决方案,坚决不过度设计。

转载地址:http://eoybx.baihongyu.com/

你可能感兴趣的文章
Android应用集成支付宝接口的简化
查看>>
[分享]Ubuntu12.04安装基础教程(图文)
查看>>
[Vim] 搜索模式(正则表达式)
查看>>
#HTTP协议学习# (二)基本认证
查看>>
Android开发之线性布局详解(布局权重)
查看>>
WCF
查看>>
django 目录结构修改
查看>>
win8 关闭防火墙
查看>>
Android实例-录音与回放(播放MP3)(XE8+小米2)
查看>>
CSS——(2)与标准流盒模型
查看>>
MYSQL 基本SQL语句
查看>>
C#中的Marshal
查看>>
linux命令:ls
查看>>
Using RequireJS in AngularJS Applications
查看>>
hdu 2444(二分图最大匹配)
查看>>
shell编程笔记六:实现ll命令
查看>>
【SAP HANA】关于SAP HANA中带层次结构的计算视图Cacultation View创建、激活状况下在系统中生成对象的研究...
查看>>
[nodejs] nodejs开发个人博客(五)分配数据
查看>>
《Linux内核修炼之道》 之 高效学习Linux内核
查看>>
Java数据持久层框架 MyBatis之API学习九(SQL语句构建器详解)
查看>>