- 求助 企业微信 自建应用引用jdk时 怎么生成签名的随机串?
求助大佬 企业微信自建应用中要获取定位,所以需要引入js-jdk; 文档:https://work.weixin.qq.com/api/doc#90001/90144/90547 [图片] 请问这个 timestamp: , // 必填,生成签名的时间戳、nonceStr: '', // 必填,生成签名的随机串 分别在哪儿获取?
2019-11-06 - 使用webpack打包多页应用
引言 对于传统的多页应用,有着诸多比较棘手的问题,如公共模块的复用、样式兼容性、语法兼容性等等问题,而我们可以通过webpack进行构建,通过预编译的方式赋予多页应用更多的功能,如:模块化、代码检查、预处理器、最新语法、模板引擎等,本文将带大家使用webpack去配置多页应用。 多页入口的配置 多页入口的配置,其实就是配置[代码]entry[代码]属性为一个对象,先来看一个例子: [代码]// webpack.config.js module.exports = { entry: { pageOne: './src/pageOne/index.js', pageTwo: './src/pageTwo/index.js', pageThree: './src/pageThree/index.js' } }; [代码] 如上配置示例告诉 webpack 需要三个独立分离的依赖图,运行后,输出如下: [代码]$ webpack Hash: abc3dca0a96fc92ffdf9 Version: webpack 4.32.2 Time: 246ms Built at: 2019-05-26 11:13:15 Asset Size Chunks Chunk Names pageOne.js 952 bytes 0 [emitted] pageOne pageThree.js 956 bytes 1 [emitted] pageThree pageTwo.js 953 bytes 2 [emitted] pageTwo Entrypoint pageOne = pageOne.js Entrypoint pageTwo = pageTwo.js Entrypoint pageThree = pageThree.js [0] ./src/pageOne/index.js 24 bytes {0} [built] [1] ./src/pageTwo/index.js 24 bytes {2} [built] [2] ./src/pageThree/index.js 25 bytes {1} [built] [代码] 之后可以在[代码]dist[代码]目录下看到,wepback生成了[代码]pageOne.js[代码]、[代码]pageTwo.js[代码]、[代码]pageThree.js[代码]这三个文件。 多页入口的配置方式 在多页应用中,一般配置多页入口,有两种配置方式: 使用配置文件进行配置 优点:入口文件可以随意放置,只需要配置正确即可 缺点:每次新增入口都需要先进行配置 约定一个目录存放入口文件 优点:约定大于配置,消除了配置成本 缺点:没有配置文件那么灵活,入口需要放置在约定目录 本文中我们将采用第2种方式进行配置。 首先,约定[代码]src/views[代码]目录为页面的入口,如下所示: [代码]src ├──views │ ├──home │ │ ├──home.js │ │ ├──home.pug 使用 pug 模板引擎 │ │ └──home.scss 使用 scss 预处理器处理样式 │ ├──page1 │ │ ├──page1.js │ │ ├──page1.pug 使用 pug 模板引擎 │ │ └──page1.scss 使用 scss 预处理器处理样式 │ ... ... [代码] 实现收集逻辑 我们会使用[代码]glob[代码]库去引入这些入口文件,关于[代码]glob[代码]的用法,请戳GitHub - isaacs/node-glob: glob functionality for node.js 新建[代码]build/entry.js[代码]文件,内容如下: [代码]// build/entry.js const path = require('path'); // 此处需要 "npm install glob" const glob = require('glob'); // 相对于根目录取路径 const resolve = dir => path.join(__dirname, '../', dir); // 收集的入口 const entry = {}; // 收集入口文件的目录 const filePaths = glob.sync(resolve('src/views/*')); filePaths.forEach(filePath => { // 目录的名称,也就是打包的文件名 const filename = path.basename(filePath); entry[filename] = `${filePath}/${filename}.js`; }); exports.getEntry = function getEntry() { return entry; }; [代码] 修改 [代码]webpack.config.js[代码]文件,内容如下: [代码]// webpack.config.js const path = require('path'); const { getEntry } = require('./build/entry'); const resolve = dir => path.join(__dirname, dir); module.exports = { entry: getEntry(), // 配置输出的路径 output: { // 输出到 dist 目录下 path: resolve('dist'), // 输出到 dist/js 目录下 filename: 'js/[name].js' } }; [代码] 实现HTML的生成 要生成HTML文件,我们需要借助[代码]html-webpack-plugin[代码]插件。 因为我们这里使用了[代码]pug[代码]模板引擎,所以需要先经过[代码]pug-loader[代码]进行解析,关于[代码]pug[代码]更多信息,请戳入门指南 – Pug。 修改 [代码]build/entry.js[代码]文件, 内容如下: [代码]// build/entry.js ... // 此处需要 "npm install html-webpack-plugin" const HtmlWebpackPlugin = require('html-webpack-plugin'); ... exports.getEntryHTMLPlugins = function getEntryHTMLPlugins() { return Object.keys(entry).map(filename => { // 根据入口文件生成 HtmlWebpackPlugin 实例 // 一个实例对应一个 html 文件 return new HtmlWebpackPlugin({ // 模板文件 template: `${path.dirname(entry[filename])}/${filename}.pug`, // 输出的文件名 filename: `${filename}.html`, chunks: [filename] }); }); }; [代码] 修改 [代码]webpack.config.js[代码]文件,内容如下: [代码]// webpack.config.js ... const { getEntry, getEntryHTMLPlugins } = require('./build/entry'); module.exports = { ..., module: { rules: [ { // 此处需要 "npm install pug pug-loader" // 配置 pug-loader 去解析转换 pug 文件 test: /\.(pug|jade)$/, loader: 'pug-loader' } ] }, plugins: [ ...getEntryHTMLPlugins() ] }; [代码] 至此,多页入口的配置已经完成。 赋予多页应用更多的功能 入口配置完成之后,我们将完善 webpack 配置,为多页应用添加语法检查、样式预处理器、以及ES6语法转换,这些功能都是通过 loader 去处理的,我们可以简单的把 loader 看成是一个转换器。 webpack 在编译时,会对 import 的模块(文件)进行检查(也就是使用配置中的 rules 里配置的 test 正则去匹配模块),如果某个文件满足 rules 配置的规则,则会使用设置的 loader 去处理这个文件。 添加语法检查 通过 eslint 对编译前的脚本进行检查,修改 [代码]webpack.config.js[代码]文件,内容如下: [代码]// webpack.config.js ... module.exports = { ..., module: { rules: [ ..., { // 对 js 后缀的文件进行检查 test: /\.js$/, // 此处需要 "npm install eslint eslint-loader" loader: 'eslint-loader', // 加载器的执行循序,这里设置先执行 // 也就是检查编译前的代码 enfore: 'pre', exclude: /(node_modules)/ } ] }, ... }; [代码] 需要在根目录下创建[代码].eslintrc[代码]文件,对 eslint 语法检查的规则进行配置,本文不对 eslint 配置做详细介绍,关于 eslint 的更多配置,请戳Configuring ESLint - ESLint - Pluggable JavaScript linter ES6语法转换 对于 js 文件,通过 babel 进行转换,修改 [代码]webpack.config.js[代码]文件,内容如下: [代码]// webpack.config.js ... module.exports = { ..., module: { rules: [ ..., { // 对 js 后缀的文件进行转换 test: /\.js$/, // 此处需要 "npm install babel-loader @babel/core" loader: 'babel-loader', exclude: /(node_modules)/ } ] }, ... }; [代码] 需要在根目录下创建[代码].babelrc[代码]文件,对 babel 的转换规则进行配置,本文不对 babel 配置做详细介绍,关于 babel 配置的更多信息,请戳Configure Babel · Babel 样式预处理器 这里样式的 loader 配置和上面两种不太一样,需要链式调用多个 loader,以确保样式能正常运作。修改 [代码]webpack.config.js[代码]文件,内容如下: [代码]// webpack.config.js ... // 此处需要 "npm install mini-css-extract-plugin" // 此插件用于提取 css const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { ..., module: { rules: [ ..., { // 对 sass 或 scss 文件进行处理 test: /\.s(c|a)ss$/, use: [ // 当配置了多个 loader 时,webpack 的处理方式是从后到前的顺序(也就是 sass-loader、postcss-loader、css-loader) // 这里先经过了 sass 解析、再通过 postcss 补全样式 // 然后通过 css-loader 去处理一些 css module 或 @import 等问题 // 最后通过插件的 loader 把样式提取出来 MiniCssExtractPlugin.loader, // 此处需要 "npm install css-loader postcss-loader sass-loader" 'css-loader', 'postcss-loader', { loader: 'sass-loader', options: { // 此处需要 "npm install dart-sass" // 这里配置 sass 的解析器为 dart-sass,而不是 node-sass,主要是安装方便和处理速度的提升 implementation: require('dart-sass') } } ] } ] }, plugins: [ ..., // 初始化插件实例,配置提出来的 css 路径和名称 new MiniCssExtractPlugin({ filename: 'css/[name].css' }) ] ... }; [代码] 之后,我们需要在根目录下创建 postcss 的配置文件 [代码]postcss.config.js[代码] 去告知 postcss 去做什么样的处理,如下所示: [代码]// postcss.config.js module.exports = { plugins: { // 此处需要 "npm install autoprefixer" // 配置自动补全插件 autoprefixer: { // 指定补全的浏览器版本,可以有效的减少补全的代码 browsers: ['> 2%', 'last 5 versions', 'IE >= 10'] } } }; [代码] 关于 browsers 匹配到的浏览器,可以戳这里进行查询:browserl.ist: A page to display compatible browsers from a browserslist string. 提取公共 js 代码 在多个页面中,我们可能会在 A 和 B 页面中都引入同一个 js 文件,为了复用这个 js 文件,我们可以通过配置,提取出该文件。 修改 [代码]webpack.config.js[代码],内容如下: [代码]// webpack.config.js module.exports = { ..., optimization: { // 提取异步加载代码至 manifest 块中 // 当我们代码中有异步加载的文件时,会在该文件内生成异步文件加载的逻辑 // 当多个文件有异步加载时,每次在该文件生成的代码实际上大部分是相同的(基本上除了 chunkId 和 chunknamne) // 所以我们可以提取出相同的逻辑,放到一个文件中进行加载,以减少单个文件大小 runtimeChunk: : { name: 'manifest' }, splitChunks: { cacheGroups: { // 提取公共的 node_modules 代码至 vendors 块中 // 通常文件中可能会通过 npm 安装一些资源包,如果有多个文件同时引入了相同的资源包,可以通过这种方式去提取该资源包,以减少单个文件的大小和复用相同的代码 vendor: { name: 'vendors', test: /[\\/]node_modules[\\/]/ } } } } }; [代码] 提取了公共的代码块之后,我们还需要去 [代码]HtmlWebpackPlugin[代码]的实例中引用这些公共的代码块,修改[代码]build/entry.js[代码]文件,内容如下: [代码]// build/entry.js ... exports.getEntryHTMLPlugins = function getEntryHTMLPlugins() { ... return new HtmlWebpackPlugin({ ..., chunks: ['mainfest', 'vendor', filename] }); }; [代码] 开发环境和生产环境的配置 开发环境和生产环境稍有不同,主要区别在于: 生产环境:代码压缩 开发环境:监听文件变动实时编译、soucemap 要针对不同环境进行编译处理,我们可以借助 node 的[代码]npm script[代码]来区分(通过加载不同的配置文件),在根目录下找到(如果已经有该文件)或新建[代码]package.json[代码]文件,内容如下: [代码]{ "scripts": { "dev": "webpack --config webpack.dev.conf.js --watch", "build": "webpack --config webpack.prod.conf.js" } } [代码] 多于多个配置文件,有部分配置可能会重复,我们可以利用[代码]webpack-merge[代码]库对相同的配置进行合并,以减少不必要的重复配置。 开发环境的配置 根目录下新建[代码]webpack.dev.conf.js[代码]文件,内容如下: [代码]// webpack.dev.conf.js // 此处需要 "npm install webpack-merge" const merge = require('webpack-merge'); const baseConf = require('./webpack.config.js'); // 在原有的基础上扩展 dev 时需要的配置 module.exports = merge(baseConf, { // webpack 4 新增的选项,会根据环境添加一些环境需要的配置 // 如: // 添加了 NamedModulesPlugin(把 chunkid 换成 chunkname) // 设置 devtool 为 eval(为每个 module 会封装到 eval 里包裹起来执行,并且会在末尾追加注释//@ sourceURL.) // 公共代码的提取配置(optimization.splitChunks 选项)等 mode: 'development' }); [代码] 关于[代码]mode[代码]属性的详细信息,请戳这里: https://webpack.js.org/configuration/mode/ 生产环境的配置 根目录下新建[代码]webpack.prod.conf.js[代码]文件,内容如下: [代码]// webpack.prod.conf.js // 此处需要 "npm install webpack-merge" const merge = require('webpack-merge'); const baseConf = require('./webpack.config.js'); // 在原有的基础上扩展 dev 时需要的配置 module.exports = merge(baseConf, { // 相比于 development,production 主要是添加了对代码的压缩 mode: 'production' }); [代码] 至此,多页应用的配置就完成的差不多了。这就是全部了吗,当然不,如果有需要比如 vue 的单文件组件(SFC)也可以通过 vue-loader 进行配置,还有开发环境的热模块替换、静态资源文件的引入等。 总结 多页入口其实就是设置[代码]entry[代码]属性为一个对象,对于这个对象,我们可以通过配置文件或者是约定目录自动匹配的方式进行获取(又或者是直接在webpack配置上写)。 webpack 中的 loader 类似一个转换器,对于满足给定规则的文件会使用相应的 loader 去处理,也就是说,不管什么类型的文件,只要有相应的 loader,就可以处理。
2019-08-28