用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 相关文章推荐
js/jQuery对象互转(快速操作dom元素)
Feb 04 Javascript
把jquery 的dialog和ztree结合实现步骤
Aug 02 Javascript
原生javascript实现简单的datagrid数据表格
Jan 02 Javascript
简单的jQuery banner图片轮播实例代码
Mar 04 Javascript
javascript截图 jQuery插件imgAreaSelect使用详解
May 04 Javascript
JavaScript中object和Object的区别(详解)
Feb 27 Javascript
JS数组搜索之折半搜索实现方法分析
Mar 27 Javascript
浅谈vue-lazyload实现的详细过程
Aug 22 Javascript
Vue结合Video.js播放m3u8视频流的方法示例
May 04 Javascript
vue.js自定义组件directives的实例代码
Nov 09 Javascript
十分钟教你上手ES2020新特性
Feb 12 Javascript
vue $router和$route的区别详解
Dec 02 Vue.js
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
Ajax PHP分页演示
2007/01/02 PHP
PHP使用DirectoryIterator显示下拉文件列表的方法
2015/03/13 PHP
PHP curl模拟登录带验证码的网站
2015/11/30 PHP
Yii中的cookie的发送和读取
2016/07/27 PHP
ThinkPHP 3使用OSS的方法
2018/07/19 PHP
Jquery 点击按钮显示和隐藏层的代码
2011/07/25 Javascript
getElementByIdx_x js自定义getElementById函数
2012/01/24 Javascript
从面试题学习Javascript 面向对象(创建对象)
2012/03/30 Javascript
javascript 手动给表增加数据的小例子
2013/07/10 Javascript
js算法中的排序、数组去重详细概述
2013/10/14 Javascript
js判断游览器类型及版本号的代码
2014/05/11 Javascript
window.location 对象所包含的属性
2014/10/10 Javascript
利用js实现禁止复制文本信息
2015/06/03 Javascript
使用EVAL处理jqchart jquery 折线图返回数据无效的解决办法
2015/11/26 Javascript
最常见和最有用的字符串相关的方法详解
2017/02/06 Javascript
使用JavaScript实现alert的实例代码
2017/07/06 Javascript
JS实现520 表白简单代码
2018/05/21 Javascript
Vue实现6位数密码效果
2018/08/18 Javascript
ES6的解构赋值实例详解
2019/05/06 Javascript
javascript异步编程的六种方式总结
2019/05/17 Javascript
解决Vue打包上线之后部分CSS不生效的问题
2019/11/12 Javascript
Vue 解决路由过渡动画抖动问题(实例详解)
2020/01/05 Javascript
基于Element封装一个表格组件tableList的使用方法
2020/06/29 Javascript
JS pushlet XMLAdapter适配器用法案例解析
2020/10/16 Javascript
总结Python编程中三条常用的技巧
2015/05/11 Python
Python UnboundLocalError和NameError错误根源案例解析
2018/10/31 Python
Python random模块制作简易的四位数验证码
2020/02/01 Python
python 服务器运行代码报错ModuleNotFoundError的解决办法
2020/09/16 Python
一款纯css3实现的动画加载导航
2014/10/08 HTML / CSS
Oracle的内存结构(Memory structures)
2015/06/10 面试题
个人贷款承诺书
2014/03/28 职场文书
2014年销售工作总结与计划
2014/12/01 职场文书
2015年大学迎新工作总结
2015/07/16 职场文书
2019年冬至:天冷暖人心的问候祝福语大全
2019/12/20 职场文书
Python中X[:,0]和X[:,1]的用法
2021/05/10 Python
mysql配置SSL证书登录的实现
2021/09/04 MySQL