您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
前端 | webpack初探
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
前端 | webpack初探
京东科技IoT团队
2020-12-30
IP归属:未知
304000浏览
前端
# webpack初探 > 1. 当前web开发面临的困境 > 1. 文件依赖关系错综复杂 > 2. 静态资源请求效率低 > 3. 模块化支持不友好 > 4. 浏览器对高级JavaScript特性兼容度较低 > 2. Webpack 是一个前端资源的打包工具,它可以将js、image、css等资源当成一个模块进行打包。 ## 概念 本质上,```webpack``` 是一个现代 ```JavaScript ```应用程序的 “静态模块打包器”。 > > > 根据文件间的依赖关系对其进行静态分析,然后将这些模块按指定规则生成静态资源,当 webpack 处理程序时,会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。 > > webpack是一个打包模块的机制, 把依赖的模块转化成可以代表这些包的静态文件。是识别 入口文件、识别模块依赖,来打包代码。 > > 至于你的代码使用的是commonjs还是amd或者es6的import。webpack都会对其进行分析。来获取代码的依赖。 > > webpack做的就是分析代码。转换代码,编译代码,输出代码。webpack是基于node开发的,所以webpack.config.js是以commonjs形式书写的(node中的模块化是commonjs规范的) > > #### 核心概念 - 入口 (entry): 入口起点指示 ```webpack``` 应该使用哪个模块,作为构建其内部依赖图的开始。```webpack ``` 会找出有哪些模块和库是入口起点依赖的。 - 输出 (output):告诉 ```webpack``` 在哪里输出它所创建的 ``` bundle```,以及如何命名这些文件。主要输出文件的默认值是 `./dist/main.js`,其他生成文件默认放置在 `./dist` 文件夹中。 - Mode:提供 `mode` 配置选项,告知 ```webpack``` 使用相应模式的内置优化。 默认 'production' , 选项: 'none' | 'development' | 'production'。 - loader:```webpack``` 只能理解 ``` JavaScript``` 和 ```JSON``` 文件,这是 ```webpack``` 开箱可用的自带能力。```loader``` 让 ```webpack``` 能够去处理其他类型的文件,并将它们转换为有效 [模块](https://webpack.docschina.org/concepts/modules),以供应用程序使用,以及被添加到依赖图中。 - 插件(plugin):插件是 ```webpack``` 生态的关键部分, 它为社区用户提供了一种强有力的方式来直接触及 ```webpack``` 的编译过程。 插件能够 [hook](https://webpack.docschina.org/api/compiler-hooks/#hooks) 到每一个编译(compilation)中发出的关键事件中。 在编译的每个阶段中,插件都拥有对 `compiler` 对象的完全访问能力, 并且在合适的时机,还可以访问当前的 `compilation` 对象。 #### > ### webpack构建流程 > > 从启动webpack构建到输出结果经历了一系列过程: > > 1. 解析webpack配置参数,合并从shell传入和`webpack.config.js`文件里配置的参数,生产最后的配置结果。 > 2. 注册所有配置的插件,好让插件监听webpack构建生命周期的事件节点,以做出对应的反应。 > 3. 从配置的`entry`入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归下去。 > 4. 在解析文件递归的过程中根据文件类型和loader配置找出合适的loader用来对文件进行转换。 > 5. 递归完后得到每个文件的最终结果,根据`entry`配置生成代码块`chunk`。 > 6. 输出所有`chunk`到文件系统。 > > 需要注意的是,在构建生命周期中有一系列插件在合适的时机做了合适的事情,比如`UglifyJsPlugin`会在loader转换递归完后对结果再使用`UglifyJs`压缩覆盖之前的结果。 ### 基础配置 #### 首先安装: webpack - 安装本地 webpack - webpack webpack-cli 4.0以后使用安装 - 安装全局可能导致版本的不一致 ``` yarn init -y 初始化 yarn add webpack webpack-cli -D // 开发依赖,生产环境不需要 ``` #### webpack 打包文件解析 webpack 零配置打包 ```js webpack 开箱即用,可以无需使用任何配置文件。webpack 会假定项目的入口起点为 ./src/index.js,然后会在 dist/main.js 输出结果,并且在生产环境开启压缩和优化。 ``` ```js 运行 npx webpack ``` webpack 高度可配置 ```js // 运行webpack 会默认 解析 webpack.config.js 的配置文件 // webpack --config xxx.config.js 指定配置文件 ``` ```js // webpack.config.js const path = require('path'); module.exports = { mode: 'development', // development,production entry: './src/index.js', output: { filename:'app.js', path: path.resolve(__dirname, 'dist'), // 绝对路径 } } ``` ```js // ./src/index.js console.log("小京鱼"); ``` ``` npm run build ``` ```js // ./src/index.js console.log("小京鱼"); // 终端执行 npm run build // 打包后文件 ./dist/app.js (() => { // 立即执行函数 eval("console.log(\"小京鱼\");\n\n//# sourceURL=webpack://demo2/./src/index.js?"); })(); // eval 函数会将传入的字符串当做 JavaScript 代码进行执行。 ``` 打包commonjs模块 ```js // index.js const a = require('./a.js'); console.log(a); ``` ```js // a.js module.exports = '小京鱼'; ``` 打包后文件 ```js (() => { var __webpack_modules__ = { "./src/a.js": (module) => { eval( "module.exports = '小京鱼';\n\n//# sourceURL=webpack://demo2/./src/a.js?" ); }, }; /************************************************************************/ // 模块缓存 var __webpack_module_cache__ = {}; // webpack 自己实现的require 方法 function __webpack_require__(moduleId) { // 检查是否在缓存中, 如果有直接返回 exports属性。 if (__webpack_module_cache__[moduleId]) { return __webpack_module_cache__[moduleId].exports; } // 如果缓存中没有该模块,创建并推进缓存中 var module = (__webpack_module_cache__[moduleId] = { // no module.id needed // no module.loaded needed exports: {}, }); // 执行 引入模块中对应的函数 __webpack_modules__[moduleId](module, module.exports, __webpack_require__); // 返回module.exports 属性。 return module.exports; } /************************************************************************/ (() => { eval( 'const a = __webpack_require__(/*! ./a.js */ "./src/a.js");\nconsole.log(a);\n\n//# sourceURL=webpack://demo2/./src/index.js?' ); })(); })(); ``` 打包es module 模块 ```js (() => { "use strict"; var __webpack_modules__ = { "./src/b.js": ( __unused_webpack_module, __webpack_exports__, __webpack_require__ ) => { eval( "__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => __WEBPACK_DEFAULT_EXPORT__\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ('小京鱼');\n\n//# sourceURL=webpack://demo2/./src/b.js?" ); }, "./src/index.js": ( __unused_webpack_module, __webpack_exports__, __webpack_require__ ) => { eval( '__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _b_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./b.js */ "./src/b.js");\n\nconsole.log(_b_js__WEBPACK_IMPORTED_MODULE_0__.default);\n\n//# sourceURL=webpack://demo2/./src/index.js?' ); }, }; /************************************************************************/ // 模块缓存 var __webpack_module_cache__ = {}; // webpack 实现的require 函数 function __webpack_require__(moduleId) { // 检查缓存中是否有模块,如果有返回 exports 属性 if (__webpack_module_cache__[moduleId]) { return __webpack_module_cache__[moduleId].exports; } // 创建模块对象,并推进缓存 var module = (__webpack_module_cache__[moduleId] = { exports: {}, }); // 执行模块 __webpack_modules__[moduleId](module, module.exports, __webpack_require__); // 返回exports属性。 return module.exports; } /************************************************************************/ /* webpack/runtime/define property getters */ (() => { // 将模块中的属性 加入 exports属性中 __webpack_require__.d = (exports, definition) => { for (var key in definition) { if ( __webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key) ) { Object.defineProperty(exports, key, { enumerable: true, get: definition[key], }); } } }; })(); /* webpack/runtime/hasOwnProperty shorthand 判断对象中是否包含某属性 */ (() => { __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); })(); /* webpack/runtime/make namespace object */ (() => { // define __esModule on exports 标记该模块为es6模块 从而执行一些特殊处理, __webpack_require__.r = (exports) => { if (typeof Symbol !== "undefined" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); } Object.defineProperty(exports, "__esModule", { value: true }); }; })(); /************************************************************************/ // startup // Load entry module __webpack_require__("./src/index.js"); })(); // Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性 ``` ### 开发中常用的配置 #### Html 插件 安装开发依赖服务, webpoack-dev-server ```js yarn add webpack-dev-server -D // 执行 npx webpakc-dev-server ``` 依赖报错处理方案 ```js yarn add webpack@4.43.0 webpack-cli@3.3.12 -D //webpack 版本过高 可以降级处理 // "webpack": "^4.43.0", // "webpack-cli": "^3.3.12", // "webpack-dev-server": "^3.11.0" // 开发服务器devSever:用来实现自动化(自动进行编译,自动打开浏览器,自动刷新浏览器) // 特点:只会在内存中进行打包,不会有任何输出 // 启动devServer指令为 npx webpack-dev-server devServer:{ // 开发服务器的配置 port: 8080, // 指定端口 progress:true, // 进度 contentBase: path.resolve(__dirname,'dist'), // 推荐绝对路径 compress: true, // 启用gzip压缩 }, ``` 安装HtmlWebpackPlugin ```js yarn add html-webpack-plugin -D ``` 配置插件 ```js plugins: [ // 数组 放着所有的插件 new HtmlWebpackPlugin({ template:'./index.html', // 模版页面 filename: 'index.html', // 打包后页面文件名 执行webpack 可以将模版文件引入 app.js 到 index.html 中。 minify: { // 压缩index.html removeAttributeQuotes: true, // 删除属性的双引号。 collapseWhitespace: true, // 折叠空行 }, hash: true, // index.html 中的 app.js 引用增加hash戳 }), ] ``` #### 样式处理器 如何配置解析css 模块 安装css-loader、style-loader ```js // loader webpack 可以使用 loader 来预处理文件。这允许你打包除 JavaScript 之外的任何静态资源。 // 1. test 属性,识别出哪些文件会被转换。 // 2. use 属性,定义出在进行转换时,应该使用哪个 loader。 yarn add css-loader style-loader -D // 1. 使用css-loader必须要配合使用style-loader: // 2. css-loader的作用是帮我们分析出各个css文件之间的关系,把各个css文件合并成一段css; // 3. style-loader的作用是将css-loader生成的css代码挂载到页面的header部分(见下图); // 4. 多个loader配合使用时,处理顺序是:从下到上,从右到左 的顺序; module: { rules:[ { test: /\.css$/, use: [{ loader: 'style-loader', },'css-loader'], }, ], } ``` 抽离css样式文件的插件: MiniCssExtractPlugin ```js yarn add mini-css-extract-plugin -D // 本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。 // 本插件基于 webpack v4 的新特性(模块类型)构建,并且需要 webpack 4 才能正常工作。 module: { // 配置如何处理模块 rules: [ // 模块解析规则 { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader'], }, ] }, plugins: [ // 配置webpack插件 new MiniCssExtractPlugin({ filename: 'app.css', // 生成css 文件名称 }), ] ``` 自动补全前缀配置 ```js yarn add postcss postcss-loader autoprefixer -D // webpack.config.js module: { rules: [ { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 1 } // 需要注意该配置, 查询参数 importLoaders,用于配置「css-loader 作用于 @import 的资源之前」有多少个 loader。 }, { // a.css b.css loader: "postcss-loader" }, ], }, ], }, // package.json "browserslist": [ // 主要是为了表示当前项目的浏览器兼容情况。 "> 1%", // 代表着全球超过1%人使用的浏览器 "last 2 versions", // 表示所有浏览器兼容到最后两个版本 "not ie <= 8", // 表示IE浏览器版本大于8(实则用npx browserslist 跑出来不包含IE9 ) "safari >= 7" // 表示safari浏览器版本大于等于7 ] // 可在前端工程下运行 npx browserslist 命令,会自动跑出当前工程的目标浏览器列表 // 使用postcs-loader需要配置 postcss.config.js 用于配置postcss-loader 需要的插件 module.exports = { plugins: [ require("autoprefixer"), ], }; ``` #### 转换es6 ```js yarn add babel-loader @babel/core @babel/preset-env -D // @babel/preset-env 是一个智能预置,允许您使用最新的JavaScript,而无需对目标环境所需的语法转换(以及可选的浏览器填充)进行微观管理。 // index.js let a = 1; // 转换为 var a = 1; class A { // 相当于 new A().a =1 需要插件进行转换 a=1; } @ClassB // 装饰器是一个处于草案中的特性需要插件 。 类装饰器 当装饰的对象是类时,我们操作的就是这个类本身,即装饰器函数的第一个参数,就是所要装饰的目标类。 class B{ constructor() { this.a = a; } } function ClassB(target) { console.log(target); } yarn add @babel/plugin-proposal-class-properties -D yarn add @babel/plugin-proposal-decorators -D { test: /\.js$/, use:[{ loader:'babel-loader', options: { presets: [ '@babel/preset-env', ], plugins:[ ['@babel/plugin-proposal-decorators',{ "legacy": true }], '@babel/plugin-proposal-class-properties', ], } }], include:path.resolve(__dirname,'src'), }, ``` 其他问题: ```js 在我们用Babel做语法转换的时候(注意,这里是单纯的做语法转换,暂时不使用polyfill补齐API),需要Babel在转换后的代码里注入一些函数才能正常工作,可以看到转换后的代码上面增加了好几个函数声明,这就是注入的函数,我们称之为辅助函数。 @babel/runtime把所有语法转换会用到的辅助函数都集成在了一起,这样就做到了复用,减少了体积。 // 浏览器会报错 index.js:3 Uncaught ReferenceError: regeneratorRuntime is not defined function *gen () { yield 1 } yarn add @babel/plugin-transform-runtime -D // 允许重用Babel注入的助手代码,以节省代码大小。 yarn add @babel/runtime // 是一个包含babel模块化运行时助手和一个版本的再生器运行时的库。 //例如: “foobar”.includes(“foo”)这样的实例方法只适用于core-js@3。如果需要polyfill,可以直接导入“core js”或使用@babel/preset env的useBuiltIns选项。 // babel 7.4.0 弃用 @babel/polyfill。 应用核心模块 import "core-js/stable"; import "regenerator-runtime/runtime"; ``` #### js语法校验 ```js yarn add eslint-loader -D // 模块解析规则 { test: /\.js$/, use:{ loader: 'eslint-loader', options: { enforce:'pre', } }, exclude: /node_modules/, }, // 下载 eslintrc.json 根目录下 修改文件名称.eslintrc.json ``` #### 图片处理 ```js yarn add file-loader -D // file-loader 文件加载程序将文件的import/require()解析为url,并将文件发送到输出目录。 // 打包图片的几种情况 // 1. js 中创建图片来引入 import photoNew from './image/new.png'; let image = new Image(); image.src = photoNew; document.querySelector('.photo-new').appendChild(image); // 2. 样式中使用的图片 body{ background: url('image/new.png') no-repeat; /* css-loader 可以解析url() 相当于require('image/new.png')*/ } // 3. html -> <img src="XXX" /> html中直接使用img标签src加载图片的话,因为没有被依赖,图片将不会被打包。 yarn add html-withimg-loader -D // html-withimg-loader 解决这个问题,图片会被打包,而且路径也处理妥当。 module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '../../' // 设置公共路径解决 图片生成的路径不对问题 } }, { loader: 'css-loader', options: { importLoaders: 1, } }, { loader: "postcss-loader" }, ], include:path.resolve(__dirname,'src'), exclude: /node_modules/ }, { test:/\.(png|jpg|gif)$/, use: { loader: 'url-loader', // 文件加载程序将文件的import/require()解析为url,并将文件发送到输出目录。 options: { name: '[name].[hash:4].[ext]', outputPath: 'image', esModule:false, limit: 20*1024 }, }, exclude: /node_modules/ }, { test:/\.html$/i, use: { loader: 'html-withimg-loader' }, exclude: /node_modules/ }, ], }, ``` #### 配置source-map ```js devtool:'source-map', // 增加映射文件,可以帮助调试代码 ``` #### webpack 跨域问题的解决 有服务端 使用 proxy 代理 ```js // server.js // 启动express 服务 监听3000 端口 const express = require('express'); const app = express(); // 创建一个get请求 app.get('/user',(req,res) => { // 返回json res.json({name: '小京鱼'}); }) app.listen(3000); // 监听3000端口 // index.js let btn = document.querySelector('#getdata'); let showdiv = document.querySelector('#show'); btn.addEventListener('click', function(){ // 监听元素的点击时间 // ajax 请求 let xhr = new XMLHttpRequest(); xhr.open('GET', '/api/user', true); xhr.onload = () => { console.log(typeof xhr.response); const data = typeof xhr.response === 'string' ? JSON.parse(xhr.response) : xhr.response; showdiv.innerHTML = data.name; } xhr.send(); }) // 配置代理 webpack.config.js devServer: { // 开发服务器的配置 port: 8080, // 指定端口 contentBase: path.resolve(__dirname, "dist"), // 推荐绝对路径 compress: true, // 一切服务都启用gzip // proxy: { // '/api': 'http://localhost:3000' // } proxy: { // 重新路径请求 '/api': { target: 'http://localhost:3000', pathRewrite: {'/api':''} } } }, ``` 单纯模拟数据 ```js devServer: { before(app){ // 提供的钩子 app.get('/api/user',(req,res) => { res.json({name: '小京鱼 --- before'}); }) } } ``` 在服务端启动 webpack ```js const express = require('express'); const webpack = require('webpack'); const middle = require('webpack-dev-middleware'); // 与webpack捆绑使用,允许服务从webpack发出的文件。这只能用于开发。 const app = express(); const config = require('./webpack.config'); const compiler = webpack(config); // 编译配置文件 app.use(middle(compiler)); // 使用中间件 app.get('/api/user',(req,res) => { res.json({name: '小京鱼 --- middle'}); }) app.listen(3000); // 监听3000端口 ``` #### webpack区分环境配置 ```js yarn add webpack-merge -D // 提供一个合并函数,用于连接数组并合并对象以创建新对象。 // 具体用法 // 将配置文件分为 base配置 、 dev开发配置 、 prod 生产配置 // webpack.base.js 公共配置 const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { entry: "./src/index.js", output: { filename: "js/app.[hash:8].js", // 文件修改后生成不同的文件名称防止缓存问题 path: path.resolve(__dirname, "dist"), }, module: { // 模块 rules: [ { test: /\.js$/, use: [ { loader: "babel-loader", options: { presets: ["@babel/preset-env"], plugins: [ ["@babel/plugin-proposal-decorators", { legacy: true }], "@babel/plugin-proposal-class-properties", ["@babel/plugin-transform-runtime"], ], }, }, ], include: path.resolve(__dirname, "src"), exclude: /node_modules/, }, { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: "../", }, }, { loader: "css-loader", options: { importLoaders: 1 }, }, { loader: "postcss-loader", }, ], include: path.resolve(__dirname, "src"), exclude: /node_modules/, }, { test: /\.(png|jpg|gif|jpeg)$/, use: { // loader: 'file-loader', // 文件加载程序将文件的import/require()解析为url,并将文件发送到输出目录。 loader: "url-loader", // 用于将文件转换为base64 URI的Web包加载程序。 options: { name: "[name].[hash:4].[ext]", outputPath: "image", esModule: false, limit: 2*1024, }, }, exclude: /node_modules/, }, { test: /\.html$/i, use: { loader: "html-withimg-loader", }, exclude: /node_modules/, }, ], }, plugins: [ // 数组 放着所有的插件 new HtmlWebpackPlugin({ template: "./index.html", filename: "index.html", minify: { removeAttributeQuotes: true, // 删除属性的双引号。 collapseWhitespace: true, // 折叠空行 }, inject: true, }), new MiniCssExtractPlugin({ filename: "css/app.[hash:4].css", }), ], }; // webpack.dev.js let {merge} = require('webpack-merge'); // 提供一个合并函数,用于连接数组并合并对象以创建新对象。 let base = require('./webpack.base'); const path = require("path"); module.exports = merge(base,{ mode: 'development', devServer: { port: 8080, // 指定端口 progress: false, contentBase: path.resolve(__dirname, "dist"), compress: true, // 一切服务都启用gzip }, devtool:'source-map', // 增加映射文件,可以帮助调试代码 module: { rules: [ { test: /\.js$/, use:{ loader: 'eslint-loader', options: { cache: false, enforce:'pre', } }, exclude: /node_modules/, }, ] }, }); // webpack.prod.js let {merge} = require('webpack-merge'); // 提供一个合并函数,用于连接数组并合并对象以创建新对象。 let base = require('./webpack.base'); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); module.exports = merge(base,{ mode: 'production', plugins: [ new CleanWebpackPlugin({ path: ".dist", }), ], }); // package.json 配置启动脚本 "scripts": { "dev": "webpack-dev-server --config webpack.dev.js --open ", "build": "webpack --config webpack.prod.js" }, ``` #### 实现 loader > ### 单个loader用法 > > - 最后的 loader 最早调用,将会传入原始资源内容。 > - 第一个 loader 最后调用,期望值是传出 JavaScript 和 source map(可选)。 > - 中间的 loader 执行时,会传入前一个 loader 传出的结果。 > 用法准则 > > - 简单 > > loaders 应该只做单一任务。这不仅使每个 loader 易维护,也可以在更多场景链式调用。 > > - 链式(Chaining) > > 利用 loader 可以链式调用的优势。写五个简单的 loader 实现五项任务,而不是一个 loader 实现五项任务 > > - 模块化(Modular) > > 保证输出模块化。loader 生成的模块与普通模块遵循相同的设计原则。 > > - 无状态(Stateless) > > 确保 loader 在不同模块转换之间不保存状态。每次运行都应该独立于其他编译模块以及相同模块之前的编译结果。 ```js yarn add loader-utils schema-utils -D // loader-utils 包。它提供了许多有用的工具,但最常用的一种工具是获取传递给 loader 的选项; //schema-utils 包配合 loader-utils,用于保证 loader 选项,进行与 JSON Schema 结构一致的校验; // JSON Schema本身就是一种数据结构,可以清晰的描述JSON数据的结构。是一种描述JSON数据的JSON数据。 // loader是导出为一个函数的node模块。该函数在loader转换资源的时候调用。给定的函数将调用loader API,并通过this上下文访问。 // custom-loader.js 给每个js 加上 一段文字 或 打印一段话 const loaderUtils = require('loader-utils'); const { validate } = require('schema-utils'); const fs = require('fs'); // node 自带的文件系统模块 function loader(source) { // source 一段用字符串来存储可执行的JavaScript脚本; const options = loaderUtils.getOptions(this); // 获取配置的options const cb = this.async(); // 告诉加载程序运行程序加载程序打算异步回调 // 设置需要验证的规则 const schema = { type: 'object', properties: { text: { type: 'string', }, filename: { type: 'string', }, } } validate(schema,options); if (options.filename) { // 异步读取文件 fs.readFile(options.filename, 'utf8', (err, data) => { cb(err,`${data}${source}`); }) } else { cb(null,`****${options.filename}****${source}`); } } module.exports = loader; // webpack.base.js rules: [ { test: /\.js$/, use: { loader:'custom-loader', options: { text: '小京鱼', filename: path.resolve(__dirname, 'publicCode.js') } }, include: path.resolve(__dirname, "src"), }, ] // 创建 publicCode.js console.log('*****欢迎使用 小京鱼IoT开放平台*****'); ``` ```js ``` #### plugin ```js // plugin是webpack生态的重要组成,它为用户提供了一种可以直接访问到webpack编译过程的方式。它可以访问到编译过程触发的所有关键事件。 1. plugin实际是一个类(构造函数),通过在plugins配置中实例化进行调用。 2. 它在原型对象上指定了一个apply方法,入参是compiler对象 3. 指定一个事件钩子,并调用内部提供的API 4. 完成操作后,调用webpack 提供的callback方法 // 一个 JavaScript class class MyExampleWebpackPlugin { // 将 `apply` 定义为其原型方法,此方法以 compiler 作为参数 apply(compiler) { // 指定要附加到的事件钩子函数 compiler.hooks.emit.tapAsync(‘MyExampleWebpackPlugin‘, (compilation, callback) => {// 使用 webpack 提供的 plugin API 操作构建结果 compilation.addModule(/* ... */); callback(); } ); } } ``` 谢谢!! > 作者:杨立坤 ###
原创文章,需联系作者,授权转载
上一篇:京东城市时空数据引擎JUST亮相中国数据库技术大会
下一篇:京东城市时空数据引擎JUST的架构设计与应用实践
相关文章
前端十年回顾 | 漫画前端的前世今生
Taro小程序跨端开发入门实战
【技术干货】企业级扫描平台EOS关于JS扫描落地与实践!
京东科技IoT团队
文章数
13
阅读量
110382
作者其他文章
01
前端 | 小程序横竖屏的坑和 rpx 布局方案
如何避免小程序开发过程中的那些“坑”
01
前端 | Chrome 80 中 Iframe cookie 无法携带的问题
Chrome 80 中 Iframe cookie 无法携带的问题求解过程。
01
NLU | 智能音箱语义理解——MDIS三合一模型
MDIS模型(Unified Model for Multiple Domain Intent and Slot)可以做到同时对话术进行领域分类、意图判断以及填槽。
01
前端 |数据大屏适配方案
数据大屏适配方案详解
京东科技IoT团队
文章数
13
阅读量
110382
作者其他文章
01
前端 | 小程序横竖屏的坑和 rpx 布局方案
01
前端 | Chrome 80 中 Iframe cookie 无法携带的问题
01
NLU | 智能音箱语义理解——MDIS三合一模型
01
前端 |数据大屏适配方案
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号