用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 相关文章推荐
javascript学习随笔(使用window和frame)的技巧
Mar 08 Javascript
页面定时刷新(1秒刷新一次)
Nov 22 Javascript
在JS数组特定索引处指定位置插入元素的技巧
Aug 24 Javascript
JavaScript DOM元素尺寸和位置
Apr 13 Javascript
JavaScript中的setUTCDate()方法使用详解
Jun 11 Javascript
学习JavaScript设计模式之状态模式
Jan 08 Javascript
标准的js无缝滚动效果
Aug 30 Javascript
jquery.validate[.unobtrusive]和Bootstrap实现tooltip错误提示问题分析
Oct 30 Javascript
JS中from 表单序列化提交的代码
Jan 20 Javascript
vue项目国际化vue-i18n的安装使用教程
Mar 14 Javascript
Vue 页面权限控制和登陆验证功能的实例代码
Jun 20 Javascript
bootstrap-treeview实现多级树形菜单 后台JSON格式如何组织?
Jul 26 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去除字符串换行符示例分享
2014/02/13 PHP
CodeIgniter框架提示Disallowed Key Characters的解决办法
2014/04/21 PHP
php 解析xml 的四种方法详细介绍
2016/10/26 PHP
在laravel中使用Symfony的Crawler组件分析HTML
2017/06/19 PHP
php-fpm开启状态统计的方法详解
2017/06/23 PHP
PHP性能分析工具xhprof的安装使用与注意事项
2017/12/19 PHP
Laravel框架处理用户的请求操作详解
2019/12/20 PHP
jQuery .tmpl() 用法示例介绍
2014/08/21 Javascript
Javascript前端UI框架Kit使用指南之kitjs的对话框组件
2014/11/28 Javascript
jquery插件之文字间歇自动向上滚动效果代码
2016/02/25 Javascript
详解webpack es6 to es5支持配置
2017/05/04 Javascript
Angular 2父子组件之间共享服务通信的实现
2017/07/04 Javascript
Vue+Flask实现简单的登录验证跳转的示例代码
2018/01/13 Javascript
vue2实现搜索结果中的搜索关键字高亮的代码
2018/08/29 Javascript
详解使用element-ui table组件的筛选功能的一个小坑
2018/11/02 Javascript
vue.js 子组件无法获取父组件store值的解决方式
2019/11/08 Javascript
详解React的回调渲染模式
2020/09/10 Javascript
vue 动态添加的路由页面刷新时失效的原因及解决方案
2021/02/26 Vue.js
Python3.0与2.X版本的区别实例分析
2014/08/25 Python
Python虚拟环境Virtualenv使用教程
2015/05/18 Python
通过Python 获取Android设备信息的轻量级框架
2017/12/18 Python
详解用Python处理HTML转义字符的5种方式
2017/12/27 Python
numpy实现合并多维矩阵、list的扩展方法
2018/05/08 Python
手把手教你Python yLab的绘制折线图的画法
2019/10/23 Python
python3中使用__slots__限定实例属性操作分析
2020/02/14 Python
django自带的权限管理Permission用法说明
2020/05/13 Python
解决keras使用cov1D函数的输入问题
2020/06/29 Python
幼儿运动会邀请函
2014/01/17 职场文书
中国梦口号
2014/06/13 职场文书
服装仓管员岗位职责
2014/06/17 职场文书
村干部群众路线教育活动对照检查材料
2014/10/01 职场文书
课堂打架检讨书200字
2014/11/21 职场文书
2014年统战工作总结
2014/12/09 职场文书
2015年学校德育工作总结
2015/04/22 职场文书
搞笑婚庆主持词
2015/06/29 职场文书
python编程实现清理微信重复缓存文件
2021/11/01 Python