主机参考:VPS测评参考推荐/专注分享VPS服务器优惠信息!若您是商家可以在本站进行投稿,查看详情!此外我们还提供软文收录、PayPal代付、广告赞助等服务,查看详情! |
我们发布的部分优惠活动文章可能存在时效性,购买时建议在本站搜索商家名称可查看相关文章充分了解该商家!若非中文页面可使用Edge浏览器同步翻译!PayPal代付/收录合作 |
简介
由于使用方便,微信小程序很快就流行起来,吸引了很多用户。 随着市场需求的快速增长,每个互联网公司都想充分利用它,而掌握小规模程序开发的艺术绝对是前端开发人员的必备技能。 然而小程序的开发一直存在一些弊端,一直被开发者所诟病。 主要症状是:
早期缺乏方便的npm包管理机制(是的,现阶段可以使用npm包,但使用起来很不方便),并且无法进行预编译。 语言处理风格不允许你使用脚本命令在不同的开发环境之间切换,并且需要你手动更改相应环境所需的配置(典型的项目至少有开发和生产环境),其实不然。可以将规范检查工具集成到项目中(例如,使用EsLint和StyleLint) 在诸多疑问之后,我开始思考如何将现代工程技术与小程序结合起来。 早期查社区资料的时候,发现我们很多前辈都是根据gulp来练习的。 对于多页面应用程序,例如小程序,gulp 的流式工作方式似乎更方便。 经过一些实践经验后,我对使用 gulp 的解决方案并不满意,因此我开始对 webpack 进行实践探索。 虽然实现起来比gulp要困难一些,但是我想选择webpack作为工程支持肯定会对以后的发展产生很大的影响。
练习
不要考虑预编译或规范。 这是一个复杂的问题。 第一个目标是如何使用webpack将源代码文件夹中的文件输出到目标文件夹。 接下来我们将一步步创建这个项目。
/* 创建项目* /$ mkdir wxmp-base$ cd ./wxmp-base/* 创建package.json */$ npm init/* 安装依赖包*/$ npm install webpack webpack[ k3]cli -- dev 复制代码
安装依赖项后,为本项目创建基本目录结构,如图所示。
上图是一个最简单的小程序,只包含一个全局应用配置文件和一个主页。 接下来是我们无论世界情况还是页面,文件类型都分为需要处理的js文件和可以原样复制的wxml、wxss、json文件。 带着这个想法,我开始创建一个用于运行 webpack 的配置文件,并在项目的根目录中创建了一个 build 目录来存储 webpack.config.js 文件。
$ mkdir build$ cd ./build$ touch 复制 webpack.config.js 代码/** webpack.config.js */const path = require('path');const CopyPlugin = require('copy[ k3 ] webpack-plugin');const ABSOLUTE_PATH = process.cwd();module.exports = { context: path.resolve(ABSOLUTE_PATH, 'src'), entry: { app: './app.js', '页面 /home/index': './pages/home/index.js' }, 输出: { 文件名: '[name].js', 路径: path.resolve(ABSOLUTE_PATH, 'dist') }, 模块 : {规则:[ { 测试:/\.js $/,排除:/node_modules/,使用:{加载器: 'babel-loader', 选项: { 预设: ['@babel/preset-env'], 插件: ['@babel/plugin-transform-runtime'], }, } , } ] }, 插件: [ new CopyPlugin([ { from: '**/*.wxml', toType: ' dir', }, { from: '**/*.wxss', toType: 'dir ', }, { from: '**/*.json& #39;, toType: 'dir', } ]) ]};复制代码
创建上述代码后,我解释一下上面的代码是做什么的?该怎么办。 :
我打算使用 app.js 和 home/index.js 作为 Webpack 构建入口,所以我在入口对象中写了两个属性。 使用此文件作为创建每个依赖项的起点。 所以当你在入口文件中引入其他文件时,引入的文件也可以被webpack处理。 该模块使用 babel- 加载器将 js 文件从 ES6 转换为 ES5,并添加了对新语法的处理。 这样我就解决了打开原生小程序的问题。regenerator- 运行时问题在开发过程中必须反复出现。 (此步骤需要安装依赖包@babel/core、@babel/preset-env、@babel/plugin-transform-runtime、@babel/runtime、babel-loader。) ]webpack-plugin 用于处理不需要重新处理的文件。 该插件可以直接将文件复制到目标目录。
一旦您了解了这些代码的实际用途,您就可以在终端中运行 webpack --config build/webpack.config.js 命令。 webpack 将您的源代码编译到 dist 文件夹中,您可以使用开发者工具运行、预览和上传该文件夹中的内容。
优化
在完成了最基本的Webpack构建策略之后,我们已经实现了app和主页的改造,但这还不够。 还有很多问题需要解决:
如果页面文件数量增加怎么办?我们如何标准化? 如何将其合并到我的项目中?如何使用环境变量?
接下来,让我们根据以上几点来实现 Webpack 策略。 升级:
页面和组件
我的第一个实现方法是用glob收集页面和组件下的js文件,创建一个工具函数,生成一个入口对象,传递给入口就是这样做的。 。 然而,在实践中我们发现这种方法有两个缺点。
如果在终端中启动该命令,此时新的页面或组件将不会自动生成新的条目。 这意味着必须重新提交订单。 一度。 工具功能被硬编码以匹配页面和组件文件夹中的文件,因此它们无助于项目的可扩展性。 如果您需要外包或重命名文件夹,则需要更改该工具的功能。
基于程序员应该非常懒惰并且不要自己做机器能做的事情的信念,我开始研究新的条目生成方案。 最后,我决定创建一个带有 webpack 构建生命周期的 webpack 插件。生成一个条目。 让我们快速解释一下代码。
/** build/entry-extract-plugin.js */const fs = require('fs');const path = require('path');const chalk = require('chalk' );const ReplaceExt = require('replace-ext');const { 差异 } = require('lodash');const SingleEntryPlugin = require('webpack/ lib/SingleEntryPlugin');const MultiEntryPlugin = require('webpack/ lib/MultiEntryPlugin' );class EntryExtractPlugin {constructor() { this.appContext = null; this.pages = []; } /** *在 app.json 文件中注册的页面和 subs 收集包并生成待处理数组 */ getPages() { const app = path.resolve(this.appContext, 'app.json'); const content = fs.readFileSync(app, 'utf8');const { 页面 = [], 子包 = [] } = JSON.parse(content); const {length:pagesLength} = 页面; if (!pagesLength) { console.log(chalk.red('"app.json 错误:缺少页面字段')); } /** 收集子包中的页面*/ const { length: subPackagesLength } = subpackages; if (subPackagesLength) { subpackages. , pages: subPages = [] } = subPackage; if (!root) { console.log(chalk.red('“app.json”中的错误“:缺少子包配置根字段')); process.exit() ; } const { length: subPagesLength } = subPages; if (!subPagesLength) { console.log(chalk.red(`“app.json”中的错误:当前分包“${root}”的页面字段为空`) ); 过程s.exit(); subPages.forEach((subPage) => Pages.push(`${root}/${subPage}`)); }); } /** *从页面开始递归搜索要使用的组件的起点*@param {String} 当前文件的上下文路径 *@param {String} 依赖路径* @param {Array} 所有包含的条目数组 */ addDependency ( context, dependentPath, Entry) { / **生成绝对路径*/ const isAbsolute = dependentPath[0] === '/'; if (isAbsolute) { 绝对路径 = path.resolve( this.appContext , dependentPath.slice(1)); } else { 绝对路径 = path.resolve(context, dependentPath); } /** 根据源代码目录生成相对路径*/ consrelativePath = path.relative(this .appContext, AbsolutePath); /** 检查路径是否有效并且在现有条目内*/ const jsPath = ReplaceExt(absolutePath, '.js'); const isQualification = fs.existsSync(jsPath); if (!isQualification) { console.log(chalk.red(`错误:在“${replaceExt(relativePath, '.js')}”中:找不到当前文件`)); ); const isExistence =entry.includes((entry) =>entry ===absolutePath); if (!isExistence) {entry.push(relativePath); } /** 获取json文件的内容* / const jsonPath = ReplaceExt(absolutePath, '.json'); const isJsonExistence = fs.existsSync(jsonPath); if (!isJsonExistence) { console.log(chalk.red(`错误: "${replaceExt(相对路径, '.json') }" in: 当前文件未找到`)); process.exit(); {const content = fs.readFileSync(jsonPath, 'utf8'); } 尝试一下。 const { usingComponents = {} } = JSON.parse(content); 常量组件 = Object.values(usingComponents); const { length } = component; /** 如果 json 文件中引用了其他组件,则执行递归 */ if (length) { constAbsoluteDir = path.dirname (absolutePath);组件) => { this.addDependency(absoluteDir, 组件, 条目); } } catch (e) { console.log(chalk.red(`错误: "${replaceExt (relativePath, ' .json'); ": 当前文件内容为空或写入错误`)); process.exit(); } } /** * 添加entry到webpack */ applyEntry(context,entryName, module) { if (Array.isArray( module)) { return new MultiEntryPlugin(context, module,entryName); 返回 new SingleEntryPlugin(context, module,entryName)。 apply(compiler) { /** 设置源代码上下文*/ const { context } = COMPILER.OPTIONS;xt = context; Compiler.hooks.entryOption.tap('EntryExtractPlugin', () => { /** 生成条目依赖数组*/ this.pages = this.getPages(); this.pages.forEach ((page ) = > void this.addDependency(context, page, this.entries)); this.entries.forEach((entry) => { this.applyEntry(context,entry, `./${entry}`) .apply( 编译器) ; }); Compiler.hooks.watchRun.tap('EntryExtractPlugin', () => { /** 检查是否添加了页面条目 */ const Pages = this.getPages() ; const diffPages =Difference(pages , this.pages); const { length } = diffPages; if (length) { this.pages = this.pages.concat(diffPages); /** 通过新的入口页面建立依赖关系 */ diffPages.forEach((page) = > 无效 this.addDependency(上下文,/** 删除与原始依赖项的交集 */ const diffEntries =Difference(entries, this.entries); diffEntries.forEach((entry) => { this.applyEntry(context,entry, `./$ {entry}`).apply(compiler); this.entries = this.entries.concat(diffEntries); }}module.exports = EntryExtractPlugin;复制代码
Webpack By Plugins 相关知识这超出了本文的范围,因此我们仅简单介绍 Webpack 插件如何干预 Webpack 工作流程并生成条目。 (如果您想了解更多信息,请给我发私信,如果我有时间,我可能会将一些信息放在一起。)这个插件实际上做了两件事。
通过entryOption使用编译器钩子递归生成条目数组。 一次将一项添加到条目中。 使用编译器的watchRun钩子来监视重新编译期间是否添加了新页面。 在这种情况下,将使用新添加的页面生成依赖项数组并将其添加到条目中。
接下来,将此插件应用到您之前的 webpack 策略中,并按如下方式更改上面的配置(不要忘记安装 chalk Replace-ext 依赖项)
/** build/webpack.config.js * / const EntryExtractPlugin = require('./entry-extract-plugin');module.exports = { ...Entry: { app: './app.js' }, Plugin: [ ... new EntryExtractPlugin() ]}Copy Code 式的预编译和 EsLint
式的预编译,而 EsLint 应用其实已经准备好了。 这里有很多好文章。 仅发布实际代码:
/** build/webpack.config.js */const MiniCssExtractPlugin = require('mini-css[k3 ]extract-plugin');module.exports = { . .. 模块:{ 规则:[ ... { 强制:'pre',测试:/\.js$/,排除:/node_modules/,加载器:'eslint-loader',选项:{ 缓存:true,修复: true, }, }, { test: /\.less$/, use: [ { loader : MiniCssExtractPlugin.loader, }, { loader: 'css-loader', }, { loader: ' less-loader', }, ], }, ] }, Plugin: [ ... new MiniCssExtractPlugin({ filename: '[name].wxss' }) ]}复制代码
策略更改后,您可以将wxss后缀的文件改为less后缀(如果想使用其他预编译语言,可以自行更改loader)。 然后在js文件中添加import'./index.less'语句。 可以看到样式文件已经成功编译生成。 定期生成样式文件的最大贡献者是 mini-css-extract- 插件工具包。 这有助于将后缀名称转换并生成到目标目录。
切换环境
切换环境变量,可以使用cross-env工具包进行配置。 将两个脚本命令添加到 package.json 文件中。
"scripts": {"dev ": "cross-env OPERATING_ENV=development webpack --config build/webpack.config.js [ k3]-watch","build": "cross-env OPERATING_ENV=生产 webpack [ k3]-config build/webpack.config.js}复制代码
所以还要更改 webpack 配置创建文件并告诉 webpack 到应用环境
/** build/webpack.config.js */const.{ OPERATING_ENV } = process.env;module.exports = { ... 模式:OPERATING_ENV,devtool:OPERATING_ENV === '生产' ? 'source-map' : &# 39;inline-source- map'}复制代码
您还可以使用命令设置 Webpack 的模式,因此通过 process.env.NODE_ENV 运行您的项目,但如果您有多个环境,例如在 UAT 测试之前,由于这种可能性,我们建议使用工具包。
JS 优化
小程序对包大小要求严格。 单个包的大小不能超过2M,所以你需要进一步优化你的JS来控制你的包的大小。 我所做的优化主要是针对运行时和多个入口页面之间引用的公共部分。 修改后的配置文件为:
/** build/webpack.config.js */module.exports = { ... optimization : { splitChunks: { cacheGroups: { commons: { chunks: 'initial', name: 'commons', minSize: 0, maxSize: 0, minChunks: 2, }, }, } , runtimeChunk: { name: 'manifest',}, },}复制代码
webpack将公共部分提取出来,并在dist文件夹根目录下生成common.js和manifest.js文件,这样就大大减少了整体项目的大小,当我运行命令时,我。看到开发者工具项目无法成功运行。
这主要是因为这样的优化导致小程序中的其他js文件失去了对公共部分的依赖。 可以通过对 Webpack 配置文件进行以下更改来解决此问题:
/** build /webpack.config.js */module.exports = { ... Output: { ... globalObject : 'global ' }, 插件: [ new webpack.BannerPlugin({ Banner: 'const commons = require("./commons");\nconst runtime = require("./runtime");', raw: true, include: 'app .js', }) ]} 复制代码并回答您的问题
许多读者可能想知道为什么我们不使用现有的框架进行开发。 许多框架已经支持这些功能。 归根结底,选择一个框架无疑是一个不错的选择,因为它为开发人员带来了很多开箱即用的便利。 然而,这种选择有其优点和缺点。 另外,我也对市场上流行的框架做了一段时间的研究和实践。 在比较早期的应用如腾讯的wepy、美团的mpvue、京东的taro、Dcloud的uni-应用中,以下几点不适合我。
黑匣子对于用户来说可能会很困难。 确定问题是出在我们自己的代码中还是框架的编译过程中。(这让我陷入了许多陷阱。)框架周围的可用资源有限。 比如UI的使用基本上都依赖官方团队的开发支持。 如果没有社区,就很难找到您需要的资源(我认为 uni[The k3]app 社区做得很好)并且您无法将它们与现有的原生资源结合起来。 这些框架从根本上基于编译原理,并提供使用 React 或 Vue 作为开发语言的能力,使原生资源无缝连接。 投资很难实现(如果你的公司已经积累了商业元素,那就很头疼了)。 最后一点,也是我最关心的一点,就是框架的升级速度能否跟上官方的迭代速度。 如果您落后现有项目该怎么办
以上是基本原因。 我想自己探索一下小程序项目(其实还有一个有趣的点,呵呵)
最后写的
上面是我对原生工程小程序的探索,也应用了一些相关的程序。我没有在这篇文章中特别提到我团队的标准风格。 如果你有兴趣,可以看看我的专栏文章《团队标准——践行风格标准》。 其实还有管理静态资源和添加项目目录。 这些细节可以根据您团队的需求进行细化和补充。 我们希望本文对需要实践该领域的团队有用。 如果您发现任何错误或需要改进的地方,请在评论中告诉我们。
下载微信应用
微信是一款移动通讯软件,支持通过手机网络发送语音消息、视频、照片和文本。 微信带来了全新的移动通信体验,您可以单独或群组聊天,还可以根据您的地理位置查找附近的人。 有需要的朋友,请快来保存您的下载体验吧!
这几篇文章你可能也喜欢:
- 微信小程序翻译功能上线:现已支持18种语言
- 如何使用微信小程序中的车牌号输入法(如何使用微信小程序中的车牌号输入法进行打印)
- 微信小程序无法获取位置信息怎么办?(微信小程序无法获取位置信息)
- 微信小程序如何实现九宫格跳(如何在小程序中配置九宫格抽奖)
- 如何调节微信小程序的亮度?
本文由主机参考刊发,转载请注明:实用---微信小程序Webpack工程考察(微信小程序开发工具需要哪些步骤?) https://zhujicankao.com/123087.html
评论前必须登录!
注册