用webpack4开发小程序的实现方法


Posted in Javascript onJune 04, 2019

哈,本人是REACT系开发者,工作中需要不停的折腾webpack,为了顺带学习VUE的开发思想和思路,顺理成章的请缨为公司小程序打个框架基础。前期也去了解了下各个小程序开发框架,大体上是通过转义的思路来解决小程序和VUE/REACT的模板、逻辑关系,不做展开讨论了。只是从本人角度分享通过webpack来构建小程序的开发架构。

通过观察小程序的原有架构,不难发现其已经是一套比较完善的mvvm架构了(类VUE),融合了VUE及REACT的一些特点(以VUE为主),但却有一些不足,缺失了前端开发人员常用的npm包的引入,动态样式的编译等等提升开发效率的工作环境、模式。因此我想如果通过webpack4来为原有架构做一个有益的补充,这样原生架构不就很完美了吗?

思路

对等编译输出小程序项目的所有文件(严格按照小程序需要的文件及目录结构输出)。js/wxs通过babel编译输出,wxml/json直接输出,wxss通过stylus编译输出(我们使用stylus开发样式),顺带使用webpack抽离公共模块文件common.js,并将runtime运行时抽离作为一个独立文件。这样既精简了代码,又享用到了webpack为我们带来的好处。嗯,看上去很简单嘛,实际上却是踩了不少的坑!脚上的茧老厚了~~~

webpack module配置

module: {
 rules: [
  {
   test: /\.(wxml|axml)/, // 为支付宝小程序留了个伏笔,哈哈
   use: [
    relativeFileLoader(isWechat ? 'wxml' : 'axml'), // 这里使用file-loader简单封装了一下
    'extract-loader',
    'html-loader'
   ]
  },
  {
   test: /\.(jp(e?)g|png|gif)$/,
   use: relativeFileLoader()
  },
  {
   test: /\.wxss$/,
   include: SRC,
   use: relativeFileLoader(),
  },
  {
   test: /\.wxs$/,
   include: SRC,
   exclude: /node_modules/,
   use: [
    relativeFileLoader(),
    {
     loader: 'babel-loader',
     options: {
      babelrc: false,
      presets: [
       'es2015', 
       'stage-0'
      ]
     },
    }
   ]
  },
  {
   test: /\.js$/,
   use: {
    loader: 'happypack/loader',
    options: {
     id: 'babel'
    }
   },
   exclude: /node_modules/,
  },
  {
   test: /\.styl$/,
   include: SRC,
   use: [
    relativeFileLoader(isWechat ? 'wxss' : 'acss'),
    'stylus-loader'
   ]
  }
 ]
},

熟悉webpack的同学通过上面的moudle配置应该能够看出资源文件编译的思路,当然直接这样配置肯定做不到正确编译,还有一些坑需要踩

全文件entry

为了对等输出,我们需要把所有文件整理为entry给webpack处理,这样的好处是js能够使用npm包,所有文件都能够支持热更新机制(webpack的热更新响应非常快,gulp的热更新很难精细控制,当项目足够大的时候,响应很慢)

function entries(dir) {
 var jsFiles = {}
 let _partten = /[\/|\\][_](\w)+/;
 let re_common = /(.*)\/common\//
 const accessExts = ['.wxml', '.wxss', '.styl', '.wxs', '.json', '.png', '.jpg', '.jpeg', '.gif']
 if (fse.existsSync(dir)) {
  globby.sync([`${dir}/**/*`, `!${dir}/js/**/cloudfunctions`, '!node_modules', `!${dir}/dist`]).forEach(function (item) {
   if (!re_common.test(item)) {
    if (!_partten.test(item)) {
     const fileObj = path.parse(item)
     const xcxSrc = path.join(dir, 'js')
     if (~item.indexOf(xcxSrc)) {
      const fileStat = fs.statSync(item)
      const relativeFile = item.replace(xcxSrc, '')
      let relativeKey = relativeFile.replace(fileObj.ext, '').substring(1)
      if (fileObj.ext == '.js') {
       jsFiles[relativeKey] = item
      }
      else {
       if (accessExts.indexOf(fileObj.ext) > -1) {
        jsFiles['nobuild__' + relativeFile] = item
       }
      }
     }
    }
   }
  })
 }
 return jsFiles
}

上述是entry的生成代码,涵盖了小程序目录结构下的所有需要的文件,并加上了一些特定的标识,以便于后续文件编译输出

非JS文件的输出

在entry方法中我们将wxml,wxss等文件作为entry统统灌给webpack去处理,正常我们使用webpack时是不会把非js文件作为entry输给webpack的。你猜webpack会报错吗,----- 哈哈,报错就讲不下去了,webpack会傻傻的把每个entry文件都当做js来对待,并且正常输出,*.wxml.js,等等,这是什么鬼,我并不需要这样的东东。加个插件来处理一下

compiler.hooks.compilation.tap('wpConcatFile', (compilation, params) => {
 compilation.hooks.beforeChunkAssets.tap('wpConcatFile', () => {
  compilation.chunks = compilation.chunks.filter(function (item) {
   return item.name.indexOf('nobuild__') == -1
  })
 })
 ...
 ...
}

nobuild__是在生成entry代码是给非js文件加上的prefix前缀,在插件中我们排除掉非js,将正常的js文件重新chunk,js文件就能够正常的输出了,那么那些非js文件呢?webpack并不会编译生成它们,中途它们就会被module中的xx-loader处理完,然后被file-loader给甩出去了。

全局变量替换

将全局变量替换为微信小程序的wx,我们通过插件解决

const globalVar = 'wx'
...
...
...
let contentObj = compilation.assets[file]
let code = contentObj.source()
code = code.replace(windowRegExp, that.globalVar);
contentObj = new RawSource(code)

compilation.assets[file] = new ConcatSource(
 contentSource,
 '\n',
 '\/**auto import common&runtime js**\/',
 '\n',
 contentObj,
);

通过上述代码不难看出,我们读取了每个文件的源码,并将全局变量window/global替换为wx,再进行源码重组。

运行时文件引入

我们需要引入runtime.js和common.js文件,runtime运行环境是webpack为每个编译文件插入的用于解析define, require, module等等这些的文件引入方法,为了精简文件,我们将之抽离为runtime.js,common.js为我们抽离出来的公共模块文件。在web/h5下引入这些资源是不是so easy,但你还记得我们是在小程序环境下嘛,并不能通过<script>标签来引入资源文件啊啊啊,你会不会猛拍脑门,一下就慌了(哈哈)。老办法,我们通过插件解决

const lens = []
let posixPath = ''
const matchIt = chunk.name.match(/\//g)
if (matchIt) {
 matchIt.forEach(it => lens.push(this.prePath))
 // posixPath = './'+lens.join('')
 posixPath = lens.join('')
} else {
 posixPath = './'
}
let posixPathFile = posixPath + 'runtime.js'
let contentSource = this.contentSource.replace('~~~~', posixPathFile)
if (chunk.name.indexOf('runtime') > -1) {
 posixPathFile = posixPath + 'common.js'
 if (hasCommon) {
  contentSource = this.contentSource.replace('~~~~', posixPathFile)
 } else {
  contentSource = ''
 }
}

上述代码片段中,posixPath是我们通过一个小的算法来推算资源引入的路径深度变量,输出并重写源文件chunk,这样我们就解决了资源引入的问题

webpack-dev-server

引入webpack-dev-server能够使得webpack的编译能够简单的输出到硬盘上,webpack默认是内存文件系统,并不输出(当然有其他方法,比如再写个插件或更换文件系统啥的),除了文件输出,webpack-dev-server还能够为我们提供mock数据服务,呵呵~,这里不展开了,大家有兴趣百度一下,还能够为我们访问后台接口作proxy,这里也不展开了。

通过上述操作,我们就能得到小程序结构的对等输出,剩下我们只需要将输出文件导入到小程序编辑器中,接下来就是开发工作了。嗯,这样就可以开始给小程序搬砖了,开心吗?

如果你想参考一下我们的编译代码,可以看这里 https://github.com/webkixi/aotoo-hub/blob/master/build/webpack.xcx.config.js

如果你想了解下我们的架构,可以看这里  https://github.com/webkixi/aotoo-hub

如果你想使用我们的架构,怕不怕?怕的话,你看着办吧,哈哈! 不怕看这里 https://www.npmjs.com/package/aotoo-cli

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
ExtJS GTGrid 简单用户管理
Jul 01 Javascript
jQuery实现隔行变色的方法分析(对比原生JS)
Nov 18 Javascript
jQuery居中元素scrollleft计算方法示例
Jan 16 Javascript
基于canvas的二维码邀请函生成插件
Feb 14 Javascript
JS闭包用法实例分析
Mar 27 Javascript
使用ionic播放轮询广告的实现方法(必看)
Apr 24 Javascript
javascript checkbox/radio onchange不能兼容ie8处理办法
Jun 13 Javascript
vue2.0中vue-cli实现全选、单选计算总价格的实例代码
Jul 18 Javascript
js事件委托和事件代理案例分享
Jul 25 Javascript
React教程之Props验证的具体用法(Props Validation)
Sep 04 Javascript
JS简单实现父子窗口传值功能示例【未使用iframe框架】
Sep 20 Javascript
elementUi vue el-radio 监听选中变化的实例代码
Jun 28 Javascript
JS实现的对象去重功能示例
Jun 04 #Javascript
JS数组中对象去重操作示例
Jun 04 #Javascript
jquery UI实现autocomplete在获取焦点时得到显示列表功能示例
Jun 04 #jQuery
IE浏览器下JS脚本提交表单后,不能自动提示问题解决方法
Jun 04 #Javascript
ES6中字符串的使用方法扩展
Jun 04 #Javascript
详解vue-cli3多页应用改造
Jun 04 #Javascript
javascript异步处理与Jquery deferred对象用法总结
Jun 04 #jQuery
You might like
php中将数组存到文件里的实现代码
2012/01/19 PHP
PHP中ob_start函数的使用说明
2013/11/11 PHP
完美的2个php检测字符串是否是utf-8编码函数分享
2014/07/28 PHP
PHP网络操作函数汇总
2015/05/18 PHP
浅谈PHP中JSON数据操作
2015/07/01 PHP
thinkphp隐藏index.php/home并允许访问其他模块的实现方法
2016/10/13 PHP
安装docker和docker-compose实例详解
2019/07/30 PHP
FLASH 广告之外的链接
2008/12/16 Javascript
JavaScript 实现类的多种方法实例
2013/05/01 Javascript
三种取消选中单选框radio的方法
2014/09/09 Javascript
Jquery实现鼠标移动放大图片功能实例
2015/03/25 Javascript
jQuery插件jRumble实现网页元素抖动
2015/06/05 Javascript
Bootstrap编写一个同时适用于PC、平板、手机的登陆页面
2016/06/30 Javascript
bootstrap的工具提示实例代码
2017/05/17 Javascript
JavaScript设计模式之调停者模式实例详解
2018/02/03 Javascript
Vue2.0结合webuploader实现文件分片上传功能
2018/03/09 Javascript
JS实现的3des+base64加密解密算法完整示例
2018/05/18 Javascript
JavaScript动态创建二维数组的方法示例
2019/02/01 Javascript
微信小程序scroll-view的滚动条设置实现
2020/03/02 Javascript
记一次react前端项目打包优化的方法
2020/03/30 Javascript
python 正则式使用心得
2009/05/07 Python
python实现批量获取指定文件夹下的所有文件的厂商信息
2014/09/28 Python
Python3读取UTF-8文件及统计文件行数的方法
2015/05/22 Python
python验证码识别的实例详解
2016/09/09 Python
python操作MySQL 模拟简单银行转账操作
2017/09/27 Python
24式加速你的Python(小结)
2019/06/13 Python
Java文件与类动手动脑实例详解
2019/11/10 Python
python 如何调用远程接口
2020/09/11 Python
为有想象力的人提供的生活方式商店:Firebox
2018/06/04 全球购物
铁路个人事迹材料
2014/01/30 职场文书
领导干部考察材料
2014/02/08 职场文书
研究生求职自荐书
2014/06/23 职场文书
优秀班组申报材料
2014/12/25 职场文书
学校光盘行动倡议书
2015/04/28 职场文书
南京南京观后感
2015/06/02 职场文书
webpack的移动端适配方案小结
2021/07/25 Javascript