关于webpack4:webpack4和webpack5打包资源文件配置差异

我的项目中会有一些资源文件,比方图片资源,字体资源文件等。这些文件在打包的时候须要挪动到dist目录webpack4中,打包这些资源文件,须要用到url-loader和file-loader,file-loader把文件输入到一个文件夹中,在代码中通过绝对 URL 去援用输入的文件 (解决图片和字体)url-loader与 file-loader 相似,区别是用户能够设置一个阈值,大于阈值会交给 file-loader 解决,小于阈值时返回文件 base64 模式编码 (解决图片和字体) { test: /\.(png|svg|gif|jpe?g)$/, use: [ { loader: "url-loader", options: { name: "img/[name].[hash:8].[ext]", // outputPath:'img' limit: 1024 * 10, }, }, ], }webpack5中内置了解决资源文件的模块asset,不再须要file-loader和url-loader。 asset/resource -->file-loader( 输入门路 )asset/inline --->url-loader(所有 data uri)asset/source --->raw-loaderasset (parser )webpack5中如果用require加载图片,须要提取default属性能力拿到资源 Img.src = require("test.png").default;或者要在css-loader中配置esModule属性为false { test: /\.css$/, use: [ "style-loader", { loader: "css-loader", options: { importLoaders: 1, // 回退一步 esModule: false, }, }, "postcss-loader", ], },图片资源文件解决如下 ...

June 22, 2022 · 1 min · jiezi

关于webpack4:迷你版webpack实现

调试webpack过程理解执行流程 开始-合并配置------------实例化compile-------设置node文件读写能力-----通过循环挂载plugins-----解决webpack外部默认的插件(入口文件) 开始-compiler.beforeRun-compiler.run--------compiler.beforeCompile-compiler.compile-------compile.make 在Compiler类中,构造函数内会挂载大量的钩子,这些钩子都来自tapable,挂载之后在后续的操作中,这些钩子期待被触发执行。 this.hooks = { /** @type {SyncBailHook<Compilation>} */ shouldEmit: new SyncBailHook(["compilation"]), /** @type {AsyncSeriesHook<Stats>} */ done: new AsyncSeriesHook(["stats"]), /** @type {AsyncSeriesHook<>} */ additionalPass: new AsyncSeriesHook([]), /** @type {AsyncSeriesHook<Compiler>} */ beforeRun: new AsyncSeriesHook(["compiler"]), /** @type {AsyncSeriesHook<Compiler>} */ run: new AsyncSeriesHook(["compiler"]), /** @type {AsyncSeriesHook<Compilation>} */ emit: new AsyncSeriesHook(["compilation"]), /** @type {AsyncSeriesHook<string, Buffer>} */ assetEmitted: new AsyncSeriesHook(["file", "content"]), /** @type {AsyncSeriesHook<Compilation>} */ afterEmit: new AsyncSeriesHook(["compilation"]), /** @type {SyncHook<Compilation, CompilationParams>} */ thisCompilation: new SyncHook(["compilation", "params"]), /** @type {SyncHook<Compilation, CompilationParams>} */ compilation: new SyncHook(["compilation", "params"]), /** @type {SyncHook<NormalModuleFactory>} */ normalModuleFactory: new SyncHook(["normalModuleFactory"]), /** @type {SyncHook<ContextModuleFactory>} */ contextModuleFactory: new SyncHook(["contextModulefactory"]), /** @type {AsyncSeriesHook<CompilationParams>} */ beforeCompile: new AsyncSeriesHook(["params"]), /** @type {SyncHook<CompilationParams>} */ compile: new SyncHook(["params"]), /** @type {AsyncParallelHook<Compilation>} */ make: new AsyncParallelHook(["compilation"]), /** @type {AsyncSeriesHook<Compilation>} */ afterCompile: new AsyncSeriesHook(["compilation"]), /** @type {AsyncSeriesHook<Compiler>} */ watchRun: new AsyncSeriesHook(["compiler"]), /** @type {SyncHook<Error>} */ failed: new SyncHook(["error"]), /** @type {SyncHook<string, string>} */ invalid: new SyncHook(["filename", "changeTime"]), /** @type {SyncHook} */ watchClose: new SyncHook([]), /** @type {SyncBailHook<string, string, any[]>} */ infrastructureLog: new SyncBailHook(["origin", "type", "args"]), // TODO the following hooks are weirdly located here // TODO move them for webpack 5 /** @type {SyncHook} */ environment: new SyncHook([]), /** @type {SyncHook} */ afterEnvironment: new SyncHook([]), /** @type {SyncHook<Compiler>} */ afterPlugins: new SyncHook(["compiler"]), /** @type {SyncHook<Compiler>} */ afterResolvers: new SyncHook(["compiler"]), /** @type {SyncBailHook<string, Entry>} */ entryOption: new SyncBailHook(["context", "entry"]) };实现迷你版webpack临时不须要这么多钩子. ...

February 2, 2022 · 2 min · jiezi

关于webpack4:寻找webpack打包入口

node_modules下.bin目录下有一个webpack.cmd脚本文件,组装定位了要执行的webpack.js文件 "%~dp0\node.exe" "%~dp0\..\webpack\bin\webpack.js" %*将启动文件指向了webpack目录下的bin\webpack.js。 在webpack.js文件中,有一个runCommond办法和isInstalled判断包是否装置函数, const runCommand = (command, args) => { const cp = require("child_process"); return new Promise((resolve, reject) => { const executedCommand = cp.spawn(command, args, { stdio: "inherit", shell: true }); executedCommand.on("error", error => { reject(error); }); executedCommand.on("exit", code => { if (code === 0) { resolve(); } else { reject(); } }); });};const isInstalled = packageName => { try { require.resolve(packageName); return true; } catch (err) { return false; }};installedClis获取曾经装置的cli,这外面重要的就是CLIs数组中第一行isInstalled("webpack-cli"),指向了webpack-cli。 ...

February 1, 2022 · 1 min · jiezi

关于webpack4:webpack底层工具库tapable基本使用

tapable工作流程 实例化hook注册事件监听通过hook触发事件监听执行懒编译生成的可执行代码Hook本职是tapable实例对象,分同步和异步,异步分并行和串行两种模式 Hook执行特点Hook:一般钩子,监听器之间相互独立不烦扰BailHook:熔断钩子,某个监听返回非undefined时后续不执行WaterfallHoook: 瀑布钩子,上一个监听的返回值可传递至下一个LoopHook:循环钩子,如果以后未返回false则始终执行 tapable库同步钩子:SynckHook,SyncBailHoook,SyncWaterfallHook,SyncLoopHook异步串行钩子:AsyncSeriesHoook,AsyncSeriesBailHook,AsyncSeriesWaterfallHook,异步并行钩子:AsyncParallerHook,AsyncParalleBailHook 同步钩子:SyncHook let { SyncHook, SyncBailHook} = require('tapable')// 创立钩子let hook = new SyncHook(['name', 'age'])hook.tap('fn1', function (name, age) { console.log('fn1', name, age)})hook.tap('fn2', function (name, age) { console.log('fn2', name, age)})hook.tap('fn3', function (name, age) { console.log('fn3', name, age)})hook.call('jack', 18)后果都能因而打印 SyncBailHook let bailHook = new SyncBailHook(['name', 'age'])bailHook.tap('fn1', function (name, age) { console.log('fn1', name, age)})bailHook.tap('fn2', function (name, age) { console.log('fn2', name, age) return 'tom' // fn1,fn2,会打印,fn3不会打印 // return undefined // fn1,fn2,fn3都会打印})bailHook.tap('fn3', function (name, age) { console.log('fn3', name, age)})bailHook.call('jack', 18)如果返回非undefined,流程就会终止 ...

January 31, 2022 · 2 min · jiezi

关于webpack4:webpack单文件懒加载分析实现

log.js文件 module.exports ="lazy load logger"index.js文件 let oBtn = document.getElementById('btn')oBtn.addEventListener('click', function () { import( /*webpackChunkName: "log"*/ './log.js').then((log) => { console.log(log) })})console.log('index.js内容')点击按钮时,控制台显示lazy load loggerwebpack在打包后,比之前的单文件加载多出了几件事件。懒加载的实现次要通过Promise来实现。实现懒加载,要调用__webpack_require__.e办法,e办法是一个promise状态保留。e办法中,jsonP创立srcipt标签,指定src,通过Promise.all把状态往后传,在过程中,动静加载要被导入的内容,这里会通过window['webpackJsonP]调用push办法.调用webpackJsonpCallback办法,执行胜利的resolve办法,__webpack_require__.e后的then办法通过__webpack_require__.t办法加载log中的内容放在value中保留.这里多出的两个操作一个是jsonP,一个是__webpack_require__.e // 定义webpackJsonpCallback,合并模块定义,扭转promise状态,还有之后的then操作 function webpackJsonpCallback(data) { // 获取要被动静加载的模块id let chunkIds = data[0]; // 获取须要被动静加载模块的依赖关系对象 let moreModules = data[1] let chunkId let resolves = [] // 循环判断chunkIds里对应的模块内容是否曾经实现加载 for (let i = 0; i < chunkIds.length; i++) { chunkId = chunkIds[i] if (Object.prototype.hasOwnProperty.call(inStalledChunks, chunkId) && inStalledChunks[chunkId]) { //数组程序,resolve,reject,promise,这里是曾经加载,所以是0 resolves.push(inStalledChunks[chunkId][0]) } // 更新chunk状态 inStalledChunks[chunkId] = 0; } for (let moduleId in moreModules) { if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { modules[moduleId] = moreModules[moduleId] } } while (resolves.length) { resolves.shift()() } } // 定义inStalledChunks 用于标识某个chunkId对应的chunk是否实现加载 let inStalledChunks = { main: 0 } // 定义jsonpScriptSrc实现src的解决 function jsonpScriptSrc(chunkId) { return __webpack_require__.p + "" + chunkId + '.built.js' } // 定义e办法,用于实现 :实现jsonP异步加载,利用promise来实现异步加载操作 __webpack_require__.e = function (chunkId) { // 定义一个数组用于寄存promise let promises = []; // 获取chunkId对应的chunk是否曾经实现了加载 let installedChunkData = inStalledChunks[chunkId] // 根据以后是否已实现加载的状态来执行后续的逻辑 if (installedChunkData !== 0) { if (installedChunkData) { // push一个残缺的promise promises.push(installedChunkData[2]) } else { // 执行加载操作 let promise = new Promise((resolve, reject) => { installedChunkData = inStalledChunks[chunkId] = [resolve, reject] }) promises.push(installedChunkData[2] = promise) // 创立标签 let script = document.createElement('script') // 设置src script.src = jsonpScriptSrc(chunkId) // 写入script标签 document.head.appendChild(script) } } // 执行promise return Promise.all(promises) }

January 31, 2022 · 1 min · jiezi

关于webpack4:根据webpack打包规则实现简单的打包手写实现

首先导入导出分为几类 CommonJS导入CommonJSEsModule导入CommonJSEsModule导入EsModule依据实现规定,简略代码样例 // index.js// import log,{age} from "./log.js"// // let log = require('./log.js')// console.log('index.js内容')// console.log("log", log,age)// log.js// CommonJS导出module.exports ="logger"// export default "jack"// export const age = 18;手写模仿实现代码 (function (modules) { // 缓存被加载过的模块 let installedModules = {}; // 定义一个__webpak_require__办法来替换 import require的加载操作 function __webpack_require__(moduleId) { // 缓存优先,判断以后缓存中是否存在要被加载的模块,如果存在就间接返回 if (installedModules[moduleId]) { return installedModules[moduleId].exports } // 如果以后缓存中不存在,就须要本人定义,执行被导入的模块内容加载 let module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} } // 调用以后moduleId对应的函数,实现内容的加载,__webpack_require__c参数存在是为了解决递归调用的问题 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__) // 当上述办法调用完结之后,批改 l 的值,标识以后模块内容曾经加载实现了 module.l = true // 加载工作实现之后,将拿回来的内容返回至调用地位 return module.exports } // 定义m属性来保留modules __webpack_require__.m = modules // 定义c属性用于保留cache __webpack_require__.c = installedModules // 定义o办法用于判断对象身上是否存在制订属性 __webpack_require__.o = function (object, property) { return Object.prototype.hasOwnProperty(object, property) } // 定义d办法用于在对象身上增加制订属性,并且给该属性提供一个getter __webpack_require__.d = function (exports, name, getter) { if (!__webpack_require__.o(exports, name)) { Object.defineProperty(exports, name, { enumerable: true, get: getter }); } } // 定义r办法用于标识以后模块EsModule __webpack_require__.r = function (exports) { if (typeof Symbol !== undefined && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: "module" }) } Object.defineProperty(exports, "__esModule", { value: true }) } // 定义n办法用于设置具体的getter __webpack_require__.n = function (module) { let getter = module && module.__esModule ? function getDefault() { return module['default'] } : function getModuleExports() { return module } __webpack_require__.d(getter, 'a', getter) return getter } // 定义p属性,用于保留资源拜访门路 __webpack_require__.p = "" // 调用__webpack_require__办法,执行模块导入和加载操作 return __webpack_require__(__webpack_require__.s = "./src/index.js")})( // CommonJS导入CommonJS // { // "./src/index.js": (function (module, exports, __webpack_require__) { // let log = __webpack_require__( /*! ./log.js */ "./src/log.js") // console.log('index.js内容') // console.log("log", log) // }), // "./src/log.js": (function (module, exports) { // // CommonJS导出 // module.exports = "logger" // }) // } // EsModule导入CommonJS { "./src/index.js": (function (module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); var _log_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( /*! ./log.js */ "./src/log.js"); var _log_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/ __webpack_require__.n(_log_js__WEBPACK_IMPORTED_MODULE_0__); console.log('index.js内容') console.log("log", _log_js__WEBPACK_IMPORTED_MODULE_0___default.a) }), "./src/log.js": (function (module, exports) { module.exports = "logger" }) } // EsModule导入EsModule // { // "./src/index.js": (function (module, __webpack_exports__, __webpack_require__) { // "use strict"; // __webpack_require__.r(__webpack_exports__); // var _log_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( /*! ./log.js */ "./src/log.js"); // console.log('index.js内容') // console.log("log", _log_js__WEBPACK_IMPORTED_MODULE_0__["default"], _log_js__WEBPACK_IMPORTED_MODULE_0__["age"]) // }), // "./src/log.js": (function (module, __webpack_exports__, __webpack_require__) { // "use strict"; // __webpack_require__.r(__webpack_exports__); // __webpack_require__.d(__webpack_exports__, "age", function () { // return age; // }); // __webpack_exports__["default"] = ("jack"); // const age = 18; // }) // })通过编译生成的index.html,引入手写built.js文件,控制台能够失常打印 ...

January 30, 2022 · 2 min · jiezi

关于webpack4:CommonJS规范下webpack单文件打包

依赖版本 "webpack": "^4.44.2","webpack-cli": "^3.3.12"一个简略的文件通过webpack打包 // 导出console.log('index.js')module.exports = '导出内容'// 导入let log = require('./log.js')console.log('index.js内容')console.log(log)打包后文件 (function (modules) { // webpackBootstrap // The module cache var installedModules = {}; // The require function function __webpack_require__(moduleId) { // Check if module is in cache if (installedModules[moduleId]) { return installedModules[moduleId].exports; } // Create a new module (and put it into the cache) var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} }; // Execute the module function // 把index.js导出内容挂载到exports上 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // Flag the module as loaded module.l = true; // Return the exports of the module return module.exports; } // expose the modules object (__webpack_modules__) __webpack_require__.m = modules; // expose the module cache __webpack_require__.c = installedModules; // define getter function for harmony exports __webpack_require__.d = function (exports, name, getter) { if (!__webpack_require__.o(exports, name)) { Object.defineProperty(exports, name, { enumerable: true, get: getter }); } }; // define __esModule on exports __webpack_require__.r = function (exports) { if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); } Object.defineProperty(exports, '__esModule', { value: true }); }; // create a fake namespace object // mode & 1: value is a module id, require it // mode & 2: merge all properties of value into the ns // mode & 4: return value when already ns object // mode & 8|1: behave like require __webpack_require__.t = function (value, mode) { if (mode & 1) value = __webpack_require__(value); if (mode & 8) return value; if ((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; var ns = Object.create(null); __webpack_require__.r(ns); Object.defineProperty(ns, 'default', { enumerable: true, value: value }); if (mode & 2 && typeof value != 'string') for (var key in value) __webpack_require__.d(ns, key, function (key) { return value[key]; }.bind(null, key)); return ns; }; // getDefaultExport function for compatibility with non-harmony modules __webpack_require__.n = function (module) { var getter = module && module.__esModule ? function getDefault() { return module['default']; } : function getModuleExports() { return module; }; __webpack_require__.d(getter, 'a', getter); return getter; }; // Object.prototype.hasOwnProperty.call __webpack_require__.o = function (object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; // __webpack_public_path__ __webpack_require__.p = ""; // Load entry module and return exports return __webpack_require__(__webpack_require__.s = "./src/index.js"); }) ({ "./src/index.js": /*! no static exports found */ (function (module, exports) { console.log('index.js内容') module.exports = '入口文件导出内容' }) });打包文件剖析特点剖析 ...

January 30, 2022 · 3 min · jiezi

关于webpack4:webpack4从0搭建组件库

代码拆散代码拆散办法有三种: 入口终点:应用 entry 配置手动地拆散代码。避免反复:应用 SplitChunksPlugin 去重和拆散 chunk。动静导入:通过模块中的内联函数调用来拆散代码。动静导入(dynamic imports)import()require.ensure预取/预加载模块(prefetch/preload module)webpack v4.6.0+ 增加了预取和预加载的反对。 在申明 import 时,应用上面这些内置指令,能够让 webpack 输入 "resource hint(资源提醒)",来告知浏览器: prefetch(预取):未来某些导航下可能须要的资源preload(预加载):以后导航下可能须要资源配置项重点详解(1)mode: "pruduction"启用 minification(代码压缩) 和 tree shaking (usedExports:true)主动指定DefinePlugin:process.env.NODE_ENV === 'production'(2)devtoolsource map 简介 生产环境:source-map开发环境:inline-source-map(3)optimizationsplitChunkssplitChunks: { chunks: 'all', // 提取公共模块 loadash // 将第三方库(library)(例如 lodash 或 react)提取到独自的 vendor chunk 文件中,是比拟举荐的做法 // 利用 client 的长效缓存机制,命中缓存来打消申请,并缩小向 server 获取资源,同时还能保障 client 代码和 server 代码版本统一。 cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'all' } }},runtimeChunkruntimeChunk: 'single' // 提取疏导模板 将 runtime 代码拆分为一个独自的 chunk(5)output// filename: '[name].[contenthash].js', // content hash 内容变动才会变动filename: 'webpack-numbers.js', path: path.resolve(__dirname, 'dist'),// 裸露 library 这是库名称 import from 'webpackNumbers'library: 'webpackNumbers', libraryTarget: 'umd'(6)pluginsHashedModuleIdsPluginconst { HashedModuleIdsPlugin } = require('webpack');plugins: [ // 不会因模块减少和缩小导致的模块内容变动,减少长缓存命中机制 new HashedModuleIdsPlugin()],BundleAnalyzerPlugin剖析打包代码 ...

November 28, 2020 · 1 min · jiezi

关于webpack4:webpack之开发环境development

一,loader加载器1,image-webpack-loader(1)目标:对图片image和svg等文件进行压缩,(2)流程:通过image-webpack-loader,打包后就会压缩,文件体积就会少上很多。vue-cli3.x的配置如下:`config.module.rule('images')//开启图片压缩.test(/.(png|jpe?g|gif)(?.*)?$/).use('image-webpack-loader').loader('image-webpack-loader').options({ bypassOnDebug: true })config.module.rule('svg')//开启svg压缩.test(/.(svg)(?.*)?$/).use('image-webpack-loader').loader('image-webpack-loader').options({ bypassOnDebug: true })`*应用过程留神点:须用cnpm装置(因为该插件注册表在cnpm,·https://developer.aliyun.com/mirror/npm/package/image-webpack-loader),如果用过yarn和npm装置过,记得卸载,而后再用cnpm装置;

September 25, 2020 · 1 min · jiezi

关于webpack4:webpack4-babel7-配置ie兼容

有一说一, IE真是让人头大????♂️????♂️以下只给出如何配置, 如果想理解更多请查看文末传送门 装置:npm i @babel/polyfill -Snpm i @babel/preset-env @babel/plugin-transform-runtime -D// 依据babel.config.json中corejs 2 / 3, 自行抉择装置 @babel/runtime-corejs2 或 @babel/runtime-corejs3npm i @babel/runtime-corejs3 -Dwebpack.config.js entry: { main: ['@babel/polyfill', './main.js'] }babel.config.json"presets": ["@babel/preset-env"],"plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": 3 } ] ]----------------------- 顺便说一下 ------------------------ 开发环境下, 在ie上可能一片空白, 有以下三种计划 build后在ie上调试 [ 可行, 但太麻烦了 ]devServer inline设置成false [ 可行, 然而iframe模式下, HRM会生效, 须要手动刷新页面 ]升高devServer版本到@2.11.1 [ 没试过 ]再说一遍, IE真真真是让人头大 ???????????? 相干材料转送:@babel/polyfill@babel/plugin-transform-runtimepreset-env与plugin-transform-runtime 应用及场景区别

September 17, 2020 · 1 min · jiezi

关于webpack4:webpack-插件开发webpackperloadparallelplugin

开发起因:因为我的项目是vue的页面级组件有差不多100个,举个case:因为用了 webpack-preftech-pulgin 插件, 页面里会插入 100个 <link href="xxx" as="script" rel="preftech">标签.通过在chrome 外面调试发现 当页面里有过多preftech 会阻塞主文件的下载速度,也就是会抢占重要资源的下载速度。所以我采纳另一种实现形式,限度最大同时预下载(未来须要)的 资源的数量,缩小因为资源下载过多,造成首页渲染过慢。包地址插件主代码 // 插件地址const fs = require('fs')const path = require('path');const Entrypoint = require('webpack/lib/Entrypoint.js');const assert = require('assert');class HtmlWebpackPreFetch { constructor(options = { paralleMax: 3 }) { this.options = options } apply(complier) { complier.hooks.compilation.tap(this.constructor.name, compilation => { // hook HtmlWebpackPlugin 输入 index.html之前的钩子,为了革新输入 index.html 文件内容(来自某个webpack 插件) let hook = compilation.hooks.htmlWebpackPluginAfterHtmlProcessing; if (!hook) { const [HtmlWebpackPlugin] = complier.options.plugins.filter( (plugin) => plugin.constructor.name === 'HtmlWebpackPlugin'); assert(HtmlWebpackPlugin, 'Unable to find an instance of ' + 'HtmlWebpackPlugin in the current compilation.'); hook = HtmlWebpackPlugin.constructor.getHooks(compilation).beforeEmit; } // 对对应的钩子 注册事件,我是这个认为???? hook.tapAsync( this.constructor.name, (htmlPluginData, callback) => { try { const { outputOptions: { publicPath = '' } , chunks, chunkGroups} = compilation; // split chunks 找到非入口文件 const sourceList = chunkGroups.filter(chunkGroup => { return !(chunkGroup instanceof Entrypoint) && !chunkGroup.isInitial() }).map(chunkGroup => chunkGroup.chunks.reduce((p, q) => { return { files: [...p.files, q.files] } , {files: []} })).reduce((p, n) => { return { files: [...p.files, ...n.files] } } , {files: []}).files.map(path => (`${publicPath}${path}`)) // loadTemplate.js 实现并发加载模版文件 let loadJsTemplate = fs.readFileSync(path.join(__dirname, './loadTemplate.js'), { encoding: 'utf-8' }) // 获取要下载的文件 和 配置参数 const templateOptions = JSON.stringify({ sourceList, ...this.options }) loadJsTemplate = loadJsTemplate.replace('/*injectOptions*/', templateOptions) let htmlTemplate = htmlPluginData.html const htmlTemplateList = htmlTemplate.split('</body>') htmlTemplateList.splice(1, 0, `<script>${loadJsTemplate}</script></body>`) htmlTemplate = htmlTemplateList.join(''); htmlPluginData.html = htmlTemplate callback(null, htmlPluginData); } catch (error) { callback(error); } } ); })}}module.exports = HtmlWebpackPreFetch并行下载文件 loadTemplate.js ...

August 28, 2020 · 3 min · jiezi

关于webpack4:webpack概念

一,模式mode1,开发模式,development包管理工具的--dev指令,是指包装置在改位子。2,生产模式,production 二,出入口入口:所有资源的被打包的入口文件进口:管制所有入口文件打包后的位子 三,loader四,Plugin1,compression-webpack-plugin事后筹备的资源压缩版本,应用 Content-Encoding 提供拜访服务⑴流程,①将文件通过算法压缩,搁置在动态服务器(nginx)上,在浏览器上输出地址后发送申请,响应头设置gzip,告知浏览器承受一个gzip资源。

August 23, 2020 · 1 min · jiezi

webpack学习笔记CodeSplitting

我的环境win 10node v10.13.0npm 6.13.4webpack 4.43.0webpack-cli 3.3.12webpack-dev-server 3.11.0webpack-merge 5.0.7引言在开发中,我们开发单页应用时,代码越来越多,还引入了很多第三方库,打包后的代码就会很大。每次要下载很久,用户访问时下载也很慢,重新访问页面时,又要加载这么大的文件。如果我们能把代码拆分开,业务逻辑代码和第三方库的代码分离,那业务改变时,只会重新加载业务逻辑代码,第三方的代码被缓存下来。如果没有webpack,我们就要手动去实现这样的功能,但是有webpack就变得很容易了。 操作首先会手动去实现代码分离,然后再利用webpack的配置,去实现相对应的效果。 1.手动实现1.1以lodash做实验,我们先安装,它是一个模块化的工具库,可以简化array、number、objects、string 的使用npm i lodash -D 1.2然后去在src目录创建一个ventor.js,书写以下内容 import _ from 'lodash'window._ = _;在src目录创建index.js书写以下内容 console.log(_.join(["1","2","3"]),"fff");console.log(_.join(["1","2","3"]),"ttt");1.3webpack的entry配置如下即可 entry:{ ventor:'./src/ventor.js', bundle:'./src/index.js', },2.通过webpack配置实现2.1index.js文件如下,删掉ventor.js import _ from 'lodash'console.log(_.join(["1","2","3"]),"fff");console.log(_.join(["1","2","3"]),"ttt");2.2entry的配置也要改一下 entry:{ // ventor:'./src/ventor.js', bundle:'./src/index.js', },2.3每次都手动实现太繁琐了,webpack可以通过配置或者plugin去实现,这里加入了optimization配置项去实现。 optimization:{ //加入CodeSpliting splitChunks:{ chunks:'all' } }, output:{ // publicPath:"./", filename: '[name].js', path: path.resolve(__dirname,'dist') },2.4运行打包命令 npm run bundle可以看见dist目录下,webpack帮我们自动拆分了代码 3.异步importwebpack4如果我们不使用同步import,也可以使用异步import(要记得配置一下babel,默认不支持这种试验性语法),使用异步import的话,webpack会自动CodeSpliting 总结这里学会了使用webpack去拆分代码,提高js的打包和加载性能。

July 7, 2020 · 1 min · jiezi

webpack4X修改SplitChunksPluginvendorsfilename报错

报错内容:You are trying to set a filename for a chunk which is (also) loaded on demand. The runtime can only handle loading of chunks which match the chunkFilename schema. Using a custom filename would fail at runtime. (cache group: defaultVendors)module.exports = { // 此处省略其他配置项 ... optimization: { splitChunks: { chunks: 'all', minSize: 30000, //minRemainingSize: 0, maxSize: 0, minChunks: 1, maxAsyncRequests: 6, maxInitialRequests: 4, automaticNameDelimiter: '~', // 下面两个属性如果设置成两个false,打包后就不会出现verndors~前缀 // cacheGroups: { // vendors: false, // default: false // } cacheGroups: { defaultVendors: { test: /[\\/]node_modules[\\/]/, priority: -10 //filename: 'vendors' }, default: { //minChunks: 2, priority: -20, reuseExistingChunk: true //filename: 'common' } } } } ...}解决办法:注释掉filename原因在文档中有: 缓存组设置filename时,在chunks项配置为inital时才会生效,我们分割同步代码时,可以设置chunk为inital,这样就可以自定义filename了。否则会报错。 ...

June 29, 2020 · 1 min · jiezi

webpack4当webpack配置文件不在根目录时如何配置CleanWebpackPlugin

新版clean-webpack-plugin在使用上一共两个变化。1.不需要指定打包文件名,他会自动找output下面的配置。2.引入时需要使用一个对象去接收目录展示 版本展示// package.json"webpack": "^4.43.0","webpack-cli": "^3.3.11","clean-webpack-plugin": "^3.0.0",导入const { CleanWebpackPlugin} = require('clean-webpack-plugin');配置// webpack.config.jsplugins: [ new webpack.ProgressPlugin(), new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: 'index.html' })],output: { filename: 'bundle.js', path: path.resolve(__dirname, '../dist')}

June 28, 2020 · 1 min · jiezi

vue单文件组件无法获取refs的问题

原文https://blog.phyer.cn/article...记录一下学习webpack+vue碰到的一个大坑,踩这个坑是我才疏学浅的表现,特此引以为戒。因为该坑实在是太坑了! 代码header.html <body> <div id="popup-wrap"> <popup ref="popup"></popup> </div></body>header.js import popup from '../../components/popup/popup.vue'import './header.scss'let header_vue;$(function () { header_vue = new Vue({ el: '#popup-wrap', data: { }, mounted: { // 输出为{popup: VueComponent} console.log(this.$refs); } components: {popup}, methods: { pop_data: function () { // 输出为{} console.log(this.$refs); } } });});export {header_vue}other.js import {header_vue} from "../header/header";$(function () { header_vue.pop_data()});popup.vue是一个普通的弹窗组件。我在header.js中引入该组件,并实例化一个header_vue使用了popup组件,然后在other.js中引入header_vue试图使用pop_data函数,该函数仅输出header_vue的$refs,经测试,该函数输出为一个空的对象,但是mounted钩子正常输出。我就很纳闷,为啥mounted输出正常,函数调用就成空的了呢,Vue也已经挂载完成了啊。 resolve一番气急败坏的debug后,在header.js的$(function())加上了一句console.log('ok'),结果浏览器输出了俩ok。短时间大脑的高速运转后,我发现了问题的所在: webpack打包了两遍header.js!所以解决的办法就是把header_vue = new Vue()改成window.header_vue = new Vue()。别处直接用就行了。 尾话目前没搞清楚具体的bug出现原因,正常使用webpack多次引入同一个export也没有出现过此问题。但是肯定是我没学明白,有大牛知道的话麻烦解答解答。

June 24, 2020 · 1 min · jiezi

基于vuecli的webpack打包优化实践及探索

转眼已经是2019年,短短三四年时间,webpack打包工具成为了前端开发中必备工具,曾经一度的面试题都是问,请问前端页面优化的方式有哪些?大家也是能够信手拈来的说出缓存、压缩文件、CSS雪碧图以及部署CDN等等各种方法,但是今天不一样了,可能你去面试问的就是,请问你是否知道webpack的打包原理,webpack的打包优化方法有哪些?所以该说不说的,笔者闲着没事研究了一下webpack的打包优化,可能大家都有看过类似的优化文章~ 但是笔者还是希望能够给大家一些新的启发~1、准备工作:测速与分析bundle既然我们要优化webpack打包,肯定要提前对我们的bundle文件进行分析,分析各模块的大小,以及分析打包时间的耗时主要是在哪里,这里主要需要用到两个webpack插件,speed-measure-webpack-plugin和webpack-bundle-analyzer,前者用于测速,后者用于分析bundle文件。 具体配置const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPluginconst smp = new SpeedMeasurePlugin({ outputFormat:"human",});module.exports = {configureWebpack: smp.wrap({ plugins: [ new webpack.ProvidePlugin({ $: "zepto", Zepto: "zepto", }), new BundleAnalyzerPlugin(), ], optimization: { splitChunks: { cacheGroups: { echarts: { name: "chunk-echarts", test: /[\\/]node_modules[\\/]echarts[\\/]/, chunks: "all", priority: 10, reuseExistingChunk: true, enforce: true, }, demo: { name: "chunk-demo", test: /[\\/]src[\\/]views[\\/]demo[\\/]/, chunks: "all", priority: 20, reuseExistingChunk: true, enforce: true, }, page: { name: "chunk-page", test: /[\\/]src[\\/]/, chunks: "all", priority: 10, reuseExistingChunk: true, enforce: true, }, vendors: { name: "chunk-vendors", test: /[\\/]node_modules[\\/]/, chunks: "all", priority: 5, reuseExistingChunk: true, enforce: true, }, }, }, }, })}由于是基于vue-cli脚手架的,所以其实vue-cli中已经帮你做了一些优化的工作,可以看到,原先项目最初的配置设置了splitchunk,进行代码分割,这在大型项目中是很有必要的,毕竟你不希望你的用户阻塞加载一个5MB大小的JS文件,所以做代码分割和懒加载是很有必要的。说远了,我们来看看这个配置,你需要用smp对配置进行再包裹,因为SpeedMeasurePlugin会对你的其他Plugin对象包裹一层代理,这样的目的是为了能够知道plugin开始和结束的时间~其次,BundleAnalyzerPlugin就跟普通的plugin一样,加载plugins数组的后面即可。接下来我们看一下最初的打包时间以及包内容分析: ...

October 13, 2019 · 2 min · jiezi

从零开始webpack4基础配置

首先看一些核心概念:1.Entry 从 webpack4 开始,不再必须定义 entry point(入口点) :它将默认为 ./src/index.js。 2.Output 输出属性规定哪里创建以及如何命名输出文件。默认./dist/main.js为主输出文件和./dist任何其他生成文件的文件夹。 3.Loaders 使用加载器来预处理文件。(如babel-loader、css-loader) 4.Plugins 插件利用webpack的编译过程执行一些任务,如优化、环境变量配置 5.Mode 通过设定mode参数设置为development,production或者none。启用对应于每个环境的WebPack内置的优化。 6.Browser Compatibility webpack支持所有符合ES5标准的浏览器(不支持IE8及以下版本)。如果要支持旧版浏览器,则需要加载polyfill以支持一些新的语法。 使用init初始化 package.json。 npm init -y安装webpack、 webpack-cli 模块 npm i webpack --save-dev在src目录下新建index.js作为入口文件。此时运行npm run build会发现已经有了dist目录。此时虽然输出了 ./dist/main.js,但是控制台提醒'mode'使用哪种模式(production(生产) 和 development(开发) 模式。)。 webpack.config.js module.exports = { entry: './src/index.js' //需要打包的文件入口}; 开箱即用,webpack不需要您使用配置文件。但是,它将假定您的项目的入口点为src/index并将结果dist/main.js缩小并优化生产。 使用webpack编译的代码中可用的所有方法。使用webpack捆绑应用程序时,您可以选择各种模块语法样式,包括ES6,CommonJS和AMD,推荐使用ES6。 //3种模块化规范//AMDdefine(function() { return function(a, b) { return a + b }})require(['./module/amd'], function(amd) { console.log(amd(1, 2))})//commonJSmodule.exports = function(a, b) { return a + b}let common = require('./module/common')console.log(common(1, 2))//ES6export default function(a, b) { return a + b}import es6 from './module/es6'console.log(es6(1, 2)) webpack.config.js ...

September 7, 2019 · 1 min · jiezi

webpack4配置进阶

description这里介绍了清除上次打包后的dist文件夹,CopyWebpackPlugin的作用,减小打包出来的bundle.js文件的大小,从bundle里抽离出css,可视化分析打包文件的webpack插件,还有webpack sourceMap定位压缩前代码错误,与配置本地代理实现开发环境跨域 清除上次打包的东西clean-webpack-plugin为什么要清理/dist文件夹?如果在多次打包后, 我们的 /dist 文件夹显得相当杂乱。webpack 将生成文件并放置在 /dist 文件夹中,但是它不会追踪哪些文件是实际在项目中用到的。 通常比较推荐的做法是,在每次构建前清理 /dist 文件夹,这样只会生成用到的文件。让我们实现这个需求。 clean-webpack-plugin 是一个流行的清理插件,安装和配置它。 npm install clean-webpack-plugin -Dwebpack.config.js const { CleanWebpackPlugin } = require('clean-webpack-plugin')module.exports = { //...other code plugins: [ new CleanWebpackPlugin({ cleanAfterEveryBuildPatterns: ['./dist'] }) ]}new CleanWebpackPlugin({ cleanAfterEveryBuildPatterns: ['./dist']})现在,执行 npm run build,检查 /dist 文件夹。如果一切顺利,现在只会看到构建后生成的文件,而没有旧文件! CopyWebpackPluginit is to copy files that already exist in the source tree, as part of the build process. 将开发环境下的目录复制到生产环境下。因为如果你需要在代码中引入静态资源文件。如果你对url的引入是相对地址 <img class="logoIcon" src="../../assets/logo.png"/> 在打包后部署到线上环境(npm run build) 或者在开发环境(npm run dev)下 会找不到资源的路径。因为打包后的目录与开发时的目录将会发生很大的变化。所以不能找到资源路径。 ...

August 28, 2019 · 3 min · jiezi

webpack4-处理图片

前言1.webpack 在处理图片的时候,会涉及一下几个问题: 图片的大小问题,比如是否压缩图片,限制图片大小图片引用方式的问题,是用base64 的格式引用图片,还是用url 路径引用图片图片路径问题,开发时写的图片路径和发布时的图片路径不同2.跟图片路径有关的文件主要有一下几类: css 里的background-imagehtml 里的<img> 标签:模板文件里的<img> 标签、主页面的<img> 标签js 里引入的img3.常用依赖 file-loader:在css 和html 主页中,相对路径的图片都会被处理,发布到输出目录中url-loader:可以替代file-loader,并且给图片一个limit 标准,当图片小于limit时,使用base64 的格式引用图片;否则,使用url 路径引用图片。image-webpack-loader:压缩图片。这个用得不算太多,因为前期可以直接让UI设计把图片压缩好,像ps 就可以自动的批量压缩图片。file-loader 示例一,安装依赖 npm i -D file-loader二,file-loader 的配置,详情参考 https://www.webpackjs.com/loa... { test: /\.(png|svg|jpg|gif)$/, use: { loader: 'file-loader', options: { name:'assets/[name].[ext]', } }},三,图片的引用 1.主页 <img src="'../../assets/sm.jpg'"/>2.模板页,模板页里的<img> 标签中相对路径的图片不会被loader 解析,因此需要使用require 引用图片 <img src="${require('../../assets/sm.jpg')}"/>3.css .layer{ width: 86px; height: 59px; background-image:url("../../assets/sm.jpg");}四,运行 webpack --mode production,图片被输出到指定目录 url-loader 示例一,安装依赖 npm i -D url-loader二,url-loader 的配置 { test: /\.(png|svg|jpg|gif)$/, use: { loader: 'url-loader', options: { name:'assets/[name].[ext]', limit:2048 } }},三,图片小于limit 时,会直接把图片的数据流,即base64 格式,写入到<img> 标签或css 中,如 ...

August 21, 2019 · 1 min · jiezi

webpack4-将html-模块化

前言webpack处理组件中的模板文件有两种方式:1.将模板文件当做一个字符串。比如html-loader:将HMTL模板文件当做一个string输出。2.将模板文件当做一个已经编译好的模板函数。比如ejs-loader:将EJS模板文件当做一个函数输出。 html-loader 示例1.安装依赖: npm i -D html-loader2.建立模板文件,目录为: ● layer.xml,后缀也可以是.html,.xml 等合法的后缀名,.xml 后缀是为了和根目录的index.html 区别开来,以便于以后对两种文件进行不同操作。就像vue 框架里的模板文件的后缀就是.vue 。 <div>hello world</div>● layer.js,引入模板文件,将其加工成对象后输出 import tpl from './layer.xml'function layer(){ return{ name:'layer', tpl:tpl }}export default layer;3.index.js 主入口文件调用模板,并将其添加到页面中 import Layer from './components/layer/Layer'const App=function(){ var dom=document.getElementById('app'); let layer=new Layer(); dom.innerHTML=layer.tpl;}new App();4.index.html 主页面 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>html页面模块化</title></head><body><div id="app"></div></body></html>5.webpack.config.js module: { rules: [ { test: /\.xml/, loader: 'html-loader' } ]},ejs-loader 示例1.安装依赖: npm i -D ejs-loader2.建立模板文件,目录为: ● ejs.tpl,模板文件的后缀也可以是.html,.xml,.ejs等合法的后缀名。ejs模板的语法还有许多,比如for 循环等,详情参考 https://ejs.bootcss.com/ ...

August 21, 2019 · 1 min · jiezi

webpack4-处理css

前言:webpack 处理css是一个很基础的话题。只是在webpack4 里,用autoprefixer解决css 的浏览器的兼容性时,会有个和以前不一样的坑。所以就再详细的写一下这方面的知识。 一,所需依赖 style-loader:将css 文件注入到html 页面的style 标签中。参考:https://www.webpackjs.com/loa...css-loader:解析import 到js 里的css 文件。参考: https://www.webpackjs.com/loa...less-loader:解析css 预处理语言,若使用的是其它的预处理语言,就要使用与其对应的loader。参考: https://www.html.cn/doc/webpa...postcss-loader:对我们在项目中写完的css 进行后期处理: 把 CSS 解析成 JavaScript 可以操作的抽象语法树结构(Abstract Syntax Tree,AST),调用插件来处理 AST 并得到结果。autoprefixer:postcss-loader的插件,为css 加前缀,以适应不同浏览器。注:postcss-loader的作用就像影视后期一样,把所有的原始文件合在一起,然后用插件加上特效,最后输出成品。autoprefixer 插件就对postcss-loader 解析出的AST 进行后期加工。 二,安装依赖 npm install --save-dev less-loader less style-loader css-loader postcss-loader autoprefixer三,建立less 测试文件 style.less #world{ display: flex;}四,在主文件index.js 中导入style.less import './style.less';五,webpack配置文件 webpack.config.js module: { rules: [ { test: /\.less$/, use: [ 'style-loader', {loader: 'css-loader', options: { importLoaders: 1 } }, 'less-loader', 'postcss-loader' ] }, ]}css-loader的 importLoaders: 1,是一个很重要的设置。这会让所有解析完成的css 只注入到一个style 标签里。若无此配置,每个新的css 文件在注入时,都会建立一个新的style 标签,有的浏览器里对style 标签是有数量限制的。 ...

August 19, 2019 · 1 min · jiezi

webpack学习笔记优化部分optimizationsplitChunks

optimization.splitChunkscacheGroups个人感觉splitChunks中除了cacheGroup之外的配置是用来作代码分割的,而cacheGroup是作为模块合并的配置项。cacheGroup内配置优先级高于外面配置,可以理解为先进行分割再进行合并,分割的代码放到哪个缓存组的块中,由优先级决定。可进行如下配置: splitChunks:{ cacheGroups: { common:{ chunks: 'initial', name:'testCommon', // 打包后的文件名 minSize: 0, minChunks: 2 // 重复2次才能打包到此模块 }, vendor: { priority: 1, // 优先级配置,优先匹配优先级更高的规则,不设置的规则优先级默认为0 test: /node_modules/, // 匹配对应文件 chunks: 'initial', name:'testVendor', minSize: 0, minChunks: 1 } } }除了test, priority和reuseExistingChunk只能卸载cacheGroups里,其他属性都能直接写在splitChunks下。 chunks前一步配置中提到chunks一般用initial打包规则,chunks可配置的参数有:all, async和initial三种。具体区别详见:https://juejin.im/post/5c08fe7d6fb9a04a0d56a702 总结一下:initial: 对于匹配文件,非动态模块打包进该vendor,动态模块优化打包async: 对于匹配文件,动态模块打包进该vendor,非动态模块不进行优化打包all: 匹配文件无论是否动态模块,都打包进该vendor webpack4的默认配置中,splitChunks.chunks默认是async,因为webpack更希望将代码中异步引入的部分作为独立模块进行打包,异步的部分在需要时引入,这种懒加载的方式更能提升页面性能。 注:import()可动态加载模块,返回一个Promise。 minSize当模块大于minSize时,进行代码分割 maxSize当模块大于maxSize时,尝试将该模块拆分,如设置maxSize为50*1024,代码中引入了jQuery,jQuery模块大于50kb,于是尝试将jQuery拆分(只是尝试,不一定真能拆分成功) maxAsyncRequests同时加载的模块数,若代码分割设置的是一个库分割成一个模块,打开某个页面时同时需要加载10个库,设置maxAsyncRequests:5,只会将那10个库分割成5个模块 maxInitialRequests最大初始化加载次数,入口文件可以并行加载的最大文件数量。maxInitialRequest和maxAsyncRequests中的'initial'和'async'代表的意思和chunks配置中的'initial'和'async'一样,maxAsyncRequests代表懒加载时最多只能同时引入多少个模块,maxInitialRequests代表非懒加载时最多能同时引入多少模块。假设maxInitialRequests设为3,有文件a.js中,使用了大量import xx from 'xx',a.js依赖的这些非动态加载的模块,最多只会被打包成3个模块。可参考:https://ymbo.github.io/2018/05/21/webpack配置代码分割/#%E4%B8%89%E3%80%81%E7%96%91%E9%9A%BE%E9%85%8D%E7%BD%AE%E9%A1%B9 automaticNameDelimiter打包的chunk名字连接符,如设为'-',生成的chunk文件名为chunk-name.js name是否以cacheGroups中的filename作为文件名 reuseExistingChunk重复使用已经存在的块,若某个模块在之前已经被打包过了,后面打包时再遇到这个模块就直接使用之前打包的模块

August 18, 2019 · 1 min · jiezi

基于webpack4的VUE多页脚手架

链接写在前面 为什么要自己手写一个脚手架?如何去思考遇到的问题?正文链接原文链接githubwhale-vue——写在前面1、为什么要自己手写一个脚手架?在写这个脚手架之前我也深深的问过自己,在我工作的项目中需要去重新写一个脚手架吗?或者说有那么多已经写好的脚手架为何不采用? 事情的经过是这样的,在很早很早以前,我尝试使用过VUE-CLI 2进行过项目开发,当时并不怎么熟悉webpack以及一些打包编译的相关知识,随着页面的增加!项目的体积的增大,导致整体build出来的包非常之大,公共文件也会随之增大,加载速度也会随之降低。后续的结果我就不做阐述了! 那么!后来... vue-Cli 3.0诞生了,首次使用简直是个救世的主,无论从速度还是编译过程体验都非常好,而且还可以通过vue.config.js自定义很多的配置,基本上完全可以自定义了,当然!也是随着页面的不断增加核项目的增大,在这个时候。我开始发现我自己对于webpack或者说打包编译的相关知识已经不能支撑我继续自定义的开发下去了。发现了一些潜在的问题,但是并没有实际的解决思路的时候,就可以追述到一些基础知识的欠缺。 随着项目的逐渐增大,尤其是多页应用的支持以及一些文件模块化的拆分,包括一些tree-shaking的运用。尽管vue-cli3.0支持configureWebpack 这样强大的API。但是仔细想想,要想从事情的本质或根本上解决问题,首先自身要相对的熟悉,并在此基础之上运用和操作,得以充分的发挥;所以还是决定自己去了解以便更好的开发。 2、如何去思考遇到的问题?在项目的开发中,尤其是在写脚手架这种工具性的东西的时候,需要考虑到的场景和实际运用的时候,更多的是不能沉浸在自己的思维之中,参考并学习别人的经验是有必要的,从而得出一套符合自己的思路。 从最开始的目录结构,以及模块化的一些思考,如何更好的做到性能的优化等等,都是值得思考的问题所在,如何处理好自己的业务逻辑,针对不同的项目以及兼容性的考虑等等。 ——正文在此之前我们需要对webpack4的一些文档或者API进行充分的了解,可以参考官方文档或者参考印记中文的webpack文档,但是针对于webpack4的文档本来介绍的不是很全面,在很多的API上面还是之前的介绍,所以,有很多小伙伴在看文档的时候发现并不能正常的进行操作,这时候可以结合两个不同版本的文档进行研究,当然时间的消耗成本也是比较高的。 3、一些基本的构建思路!在此之前我将控制业务逻辑的代码进行分离,脚手架是单独存在的。两者目录结构相互独立,业务逻辑的代码永远不会干涉到脚手架的对于一些最基础的配置我就不一一讲述了: webpack.config.jsmodule.exports = { mode:'development', entry: './***.js', output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, module:{ //... }, plugins:[ //... ]};以上是一些基本的配置方式,当然我们可以通过package.json文件中的scripts选项自定义一些基于命令行的配置: package.josn "scripts": { "dev": "APP_ENV=dev webpack-dev-server --config core/dev.js", "prd": "APP_ENV=build webpack --config core/build.js", "build": "APP_ENV=build webpack --config core/build.js", "lint": "eslint --fix */*.vue *.js", "test:whale": "jest tests/*.js && npm run build" },以上的一些配置仅供参考,分别可以通过npm run dev | npm run prd | ...来进行相关的操作。关于更多的npm script 详细参考 ...

July 15, 2019 · 2 min · jiezi

webpack-3x-到webpack-4x-踩坑记录

从jspang的《Webpack3.X版 成神之路》webpack入门(http://jspang.com/posts/2017/...)如今升级到4x,菜鸟独自踩坑一把辛酸泪 1.安装webpackwebpack 4x webpack和webpack-cli要分开安装,不再一次安装只在本地项目安装了webpack和webpack-cli npm install webpack --save-devnpm install webpack-cli --save-dev查看webpack版本时用npx webpack -v想一步到位,最好再全局安装一次 npm install webpack -gnpm install webpack-cli -g2.Webpack打包命令更改webpack 3x打包命令 webpack {entry file} {destination for bundled file}{entery file}:入口文件的路径,本文中就是src/entery.js的路径;{destination for bundled file}:填写打包后存放的路径。 webpack 4x打包命令更加严格严格区分开发与生产环境,mode可以指定 production 或 development,不指定默认为 production。 webpack {entry file} --output-filename {destination for bundled file} --output-path --mode development简写:webpack {entry file} -o {destination for bundled file} --mode development 同样我们可以在package.json里配置,简化命令 "dev": "webpack --mode development","build": "webpack --mode production"

July 12, 2019 · 1 min · jiezi

利用批处理生成前端单页或多页应用webpack40-vue20

批处理前端现在在做项目的时候大多数遇到的都是单页面应用,但有时需要做多页面的时候,会把单页拿过来修改成多页面,如果代码多了,对单页或多页的配置可能会混乱,那么有没有更好的方式能把单页面和多页面不同的配置代码分开,能更清楚的分辩他们的区别,这里是利用批处理对前端构建进行部署 git地址 目录分为三块 single //单页代码 share // 共用代码 many //多页代码只需要用到批处理对其中两者进行合并就能生成想要的单页或多页应用,提示需要安装国内的npm淘宝镜像 如果未安装的需要自行修改build.bat里的命令行call cnpm install为call npm install如下所示: 先选择存放路径,输入项目名,选择要生成的是单页还是多页 这里以单页为示例,其实就是简单的对文件进行复制,复制完成后会自动安装依赖 安装完依赖后还会自动运行项目 如上开启的项目端口为8080目录如下 webpack4 共同配置(share)这里用到了最新的webpack4.0,它简化了很多配置,多线程输出,更快的构建能力,大大提高了开发的效率首先看下配置文件config.js const path = require('path'), config = { //开发环境配置 dev: { port: 8080, // 接口代理 proxyTable: { '/v2': { target: 'https://api.douban.com', changeOrigin: true }, }, }, //生产环境配置 build: { packName: 'myProjcet', //项目打包后名称 outputPath: '', //打包后项目输出路径 templatePath: 'template.html', //html模版路径,基于根路径下 htmlShortPath: '/', //html文件输出路径, 基于outputPath resourcesPath: '', //最终打包路径 resourcesShortPath: 'resources', //资源目录 {packName}/resources }, switchVal: { to_rem: false, //是否开启px转rem to_eslint: false, //是否开启eslint语法检测 }, };//输出的目录config.build.outputPath = path.resolve(__dirname, '../../dist/', config.build.packName);//最终输出目录项目存放路径config.build.resourcesPath = path.join(config.build.outputPath, config.build.resourcesShortPath);module.exports = config;这里有开发环境下的接口代理, 生产环境的目录名称和路径 还有可选的是否转换页面字体为rem和eslint语法检测 eslint校验是默认的规则校验 它还有其它的三种通用规则 可根据自身喜好去设置 ...

June 27, 2019 · 5 min · jiezi

React搭建个人博客一项目简介与React前端踩坑

一.简介项目最开始目的是为了熟悉React用法,之后加入的东西变多,这里分成三部分,三篇博客拆开来讲。 前端部分 [x] React[x] React-Router4.0[x] Redux[x] AntDesign[x] webpack4后端部分 [x] consul+consul-template+nginx+docker搭建微服务[x] cdn上传静态资源[x] thinkJs运维部分 [x] daocloud自动化部署[x] Prometheus+Grafana监控系统博客网站分为两个部分,前台博客展示页面,后台管理页面。先看下效果:前台页面:也可以看我线上正在用的博客前台,点这里后台页面: 这一篇只讲前端部分功能描述 [x] 文章列表展示[x] 登录管理[x] 文章详情页展示[x] 后台文章管理[x] 后台标签管理[x] MarkDown发文项目结构前后台页面项目结构类似,都分为前端项目和后端项目两个部分。前端项目开发环境使用webpack的devserver,单独启动一个服务,访问后端服务的接口。生产环境直接打包到后端项目指定目录,线上只启动后端服务。前台页面:前端项目代码地址 在这里。后端项目代码地址在这里。后台页面:前端项目代码地址 在这里。后端项目代码地址在这里。 二.React踩坑记录这里讲一下项目中React踩坑和一些值得留意的问题。 1.启动报错如下:下图是一开始blog-react项目一个报错因为项目里我使用webpack来运行项目,以及添加相关配置,而不是刚建好项目时的react命令,所以一开始启动的时候会报 Module build failed: SyntaxError: Unexpected token 错误。说明ReactDom.render这句话无法解析。解决方法,添加babel的react-app预设,我直接加到了package.json里。 "scripts": { "start": "cross-env NODE_ENV=development webpack-dev-server --mode development --inline --progress --config build/webpack.config.dev.js", "build": "cross-env NODE_ENV=production webpack --env=test --progress --config ./build/webpack.config.prod.js", "test": "react-scripts test", "eject": "react-scripts eject" }, "babel": { "presets": [ "react-app" ] },2.React 组件方法绑定this问题React跟Vue不一样的一点是,组件里定义的方法,如果直接调用会报错。比如: ...

June 19, 2019 · 5 min · jiezi

vuecliwebpack初始项目

@vue/cli+webpack初始项目 (1)安装Vue-cli npm install -g @vue/cli(2)创建webpack管理的项目 vue init webpack vue_demo创建完成后,运行: npm run dev或者 npm start

June 9, 2019 · 1 min · jiezi

Webpack-4手工搭建深度分析

前言这是一篇关于webpack 4手工搭建重点问题的分析,webpack 3相关可以戳这里:https://github.com/Eleven90/webpack-pages-V3,这里并不试图从零手把手去堆代码,而是对其中的重点问题做稍微深入一点的解读。某些细节这里如果没有提及,项目代码里可能会有。项目地址:https://github.com/Eleven90/webpack-template为懒人准备的 webpack 配置模版,可以直接用于生产。这里单纯只做webpack打包的配置、前端工程化代码的组织等,有意抛开三大框架,从原始的H5出发去组织代码,关于React、Vue等配置并不复杂,可以在开发需要时添加。技术栈es6 + less + art-template + webpack 4普通 H5 开发中,引入组件化;引入 art-template 前端渲染引擎——目前前端模版里速度最快;配置 dev-server 调试模式,proxy 代理接口调试;配置 watch 模式,方便在生产环境中用 Charles 映射到本地文件;optimization 配置提取 runtime 代码;splitChunks 拆分代码,提取 vendor 主要缓存包,提取 common 次要缓存包;支持多页、多入口,自动扫描,可无限层级嵌套文件夹;MockJs 模拟 mock 数据;运行命令推荐使用yarn进行包管理,项目在某个时间节点被我切换到了yarn,但写文档时仍然是npm,对应变更一下即可。# 如果不熟悉或不想尝试yarn,直接npm install,然后npm run dev即可,不会有任何副作用yarn / yarn install # 安装全部依赖包yarn dev # 启动本地调试yarn dev-mock # 启动本地调试,MockJs模拟接口数据yarn dev:page-a # 启动本地调试,仅page-a页面yarn dev:page-b # 启动本地调试,仅page-b页面yarn build-dev # 打包代码,publicPath以/打头(可通过本地起服务访问build后的代码)yarn http-server # 启动http-server服务器,可用来访问yarn build-dev打包的代码yarn build-test # 打包测试环境代码yarn build # 打包生产环境代码# watch模式,移除了js、css的压缩,节省时间(watch时需要build压缩版代码,可自行修改)。yarn watch-dev # 启动watch模式,本地开发环境(通常用不上)yarn watch-test # 启动watch模式,测试环境yarn watch # 启动watch模式,生产环境# 如果你想用npm的话...(建议把yarn.lock文件也删掉)npm install # 安装全部依赖包npm run dev # 启动本地调试npm run dev-mock # 启动本地调试,MockJs模拟接口数据npm run dev:page-a # 启动本地调试,仅page-a页面npm run dev:page-b # 启动本地调试,仅page-b页面npm run build-dev # 打包代码,publicPath以/打头(可通过本地起服务访问build后的代码)npm run http-server # 启动http-server服务器,可用来访问npm run build-dev打包的代码npm run build-test # 打包测试环境代码npm run build # 打包生产环境代码# watch模式,移除了js、css的压缩,节省时间(watch时需要build压缩版代码,可自行修改)。npm run watch-dev # 启动watch模式,本地开发环境(通常用不上)npm run watch-test # 启动watch模式,测试环境npm run watch # 启动watch模式,生产环境Yarn和NPM的选择?通常使用NPM做包管理,但此项目使用Yarn,因为Yarn有:速度快、可离线安装、锁定版本、扁平化等更多优点。如果需要从npm切换到yarn,或者从yarn切换到npm时,请整体移除node_modules目录,及yarn.lock/package-lock.json文件,因yarn和npm两者的策略不同,导致相同的package.json列表安装后的node_modules结构是不同的(虽然这并不会引发bug,但建议在切换时清除后重新install)。Yarn常用命令yarn / yarn install // 安装全部(package.json)依赖包 —— npm installyarn run [dev] // 启动scripts命令yarn add [pkgName] // 安装依赖包(默认安装到dependencies下) —— npm install [pkgName]yarn add [pkgName]@[version] // 安装依赖包,指定版本 —— npm install [pkgName]@[version]yarn add [pkgName] -D // 安装依赖包,指定到devDependencies —— npm install [pkgName] -Dyarn remove [pkgName] // 卸载依赖包 —— npm uninstall [pkgName]yarn upgrade [pkgName] // 升级依赖包 —— npm update [pkgName]yarn upgrade [pkgName]@[version] // 升级依赖包,指定版本参考文档yarn中文网yarn安装 (预警:如果本机已经安装过NodeJS,使用brew安装yarn时,推荐使用brew install yarn --without-node命令,否则可能导致其它bug。)yarn命令yarn和npm命令对比Babel 转码这是最新的 babel 配置,和网上的诸多教程可能有不同,可以自行测试验证有效性。基础依赖包 ...

June 1, 2019 · 12 min · jiezi

探索webpack运行时

前言本篇文章建议亲自动手尝试. 最近研究了 webpack 运行时源码, 在这篇文章中记录了我的探索 webpack 这个复杂的玩具方式, 并且以图形的形式将 webpack 运行时的流程给记录的下来. 我们讨论的是什么这篇文章主要记录的是 webpack 在将文件打包后是如何在浏览器中加载以及解析的流程. 手段webpack 实在是太复杂的了, 为了避免额外的干扰, 我使用最小化实例的方式来进行探究 webpack 是如何加载和解析模块文件的. 具体的手段:首先准备几个非常简单的 js 文件, 其次准备其对应的 webpack 配置文件, 逐一测试后观察其输出, 阅读其源码, 然后得出结论. 简单总结webpack 的运行时在解析同步模块的时候就和 nodejs 规则如出一辙. 什么意思, 每一个模块在运行前都会被一个函数进行包裹: (function (module, exports, __webpack_require__) { // do something})看起来和 nodejs 一样, 作用起来也一致, 这里的当前模块的导出模块和导入模块就是: exports_webpack_require_而当一个模块被需要的时候, webpack 就会执行这个函数获取其对应的 exports对象. 注意:我们需要的是模块执行完成后的 exports 对象而不是这个模块函数. 在 webpack 中我们可以使用 ESM 规范和 commonjs 规范来加载模块, 无论使用哪种规范, 实际上都可以被这种方式来包裹起来, 因为这两种常见的方式中都存在着相同的导入导出的概念, 所以 webpack 将这两种形式进行了统一包装消除了差异. ...

May 21, 2019 · 3 min · jiezi

webpack4学习笔记一

前言这是我花了几个星期学习webpack4的学习笔记。内容不够细,因为一些相对比较简单的,就随意带过了。希望文章能给大家带来帮助。如有错误,希望及时指出。例子都在learn-webpack仓库上。如果你从中有所收获的话,希望你能给我的github点个star。 小知识npm info webpack 查看webpack的历史版本信息等npm init -y 跳过那些选项,默认全局安装的webpack : webpack index.js 打包项目中安装的webpack: npx webpack index.js 打包script中脚本打包 : npm run build命令行中打包:npx webpack index.js -o bundle.js 入口是index.js, 出口是bundle.jswebpack4设置mode:production 会压缩代码 development 就不压缩代码打包 output里面[name].js loader中的name变量 其实就是entry:{main: index.js} 中的key => main source-mapdevtool: source-mapsource-map: dist文件夹里会多生成个map后缀文件,这样页面报错的时候,点击报错后面的地址,会跳转到代码写的地方。而不会跳转到打包后的代码里。inline-source-map: 不会新生成.map文件,会插入到打包的文件底部cheap-inline-source-map: 因为inline-source-map报错会告诉你第几行第几个字符。前面加上cheap的话 只会告诉你第几行cheap-module-inline-source-map: 本来map只会映射打包出来的index.js跟业务代码中的关系。第三方引入库报错映射不到。中间加了module这个参数就可以了。比如loader也会有source-map开发的时候建议使用:cheap-module-eval-source-map,eval表示不会独立生成map文件,而是打包进代码里。一般development环境用 cheap-module-eval-source-mapproduction环境用cheap-module-source-map loader// style.cssbody { color: red;}当你想给项目添加样式的时候,import ./style.css导入css,并执行打包命令的时候。页面并报错 ERROR in ./style.css 1:5Module parse failed: Unexpected token (1:5)You may need an appropriate loader to handle this file type.这个时候就需要使用loader来编译了。安装:npm i style-loader css-loader -D为什么还需要安装style-loader呢?因为style-loader会将你的样式通过style标签插入到页面中配置webpack.config.js ...

May 15, 2019 · 2 min · jiezi

webpack4学习笔记三

前言这是我花了几个星期学习webpack4的学习笔记。内容不够细,因为一些相对比较简单的,就随意带过了。希望文章能给大家带来帮助。如有错误,希望及时指出。例子都在learn-webpack仓库上。如果你从中有所收获的话,希望你能给我的github点个star。 library当你要开发第三方库供别人使用时,就需要用到library和libraryTarget这两个配置了。 library output: { filename: 'library.js', library: 'library', libraryTarget: 'umd'},library: 当配置了这个library参数后,会把library这个key对应的value即上面代码library挂载到全局作用域中。html用script标签引入,可以通过访问全局变量library访问到我们自己开发的库。 libraryTarget:这个默认值为var。意思就是让library定义的变量挂载到全局作用域下。当然还有浏览器环境的window,node环境的global,umd等。当设置了window、global,library就会挂载到这两个对象上。当配置了umd后,你就可以通过import,require等方式引入了。 externals exterals是开发公共库很重要的一个配置。当你的公共库引入了第三方库的时候,公共库会把该第三方库也打包进你的模块里。当使用者也引入了这个第三方库后,这个时候打包就会又打了一份第三方库进来。 所在在公共模块库中配置如下代码 externals: { // 前面的lodash是我的库里引入的包名 比如 import _ from 'lodash' // 后面的lodash是别人业务代码需要注入到他自己模块的lodash 比如 import lodash from 'lodash',注意不能import _ from 'lodash',因为配置项写了lodash 就不能import _ lodash: 'lodash'},前面的lodash是我的库里引入的包名 比如 import _ from 'lodash',后面的lodash是别人业务代码需要注入到他自己模块的lodash 比如 import lodash from 'lodash',注意不能import _ from 'lodash',因为配置项写了lodash 就不能import _。 本人做了个试验,当自己开发的包不引入lodash,业务代码中也不引入lodash,那么打包业务代码的时候,webpack会把lodash打进你业务代码包里。 当然externals,配置还有多种写法,如下 externals: { lodash: { commonjs: 'lodash', commonjs2: 'lodash', amd: 'lodash', root: '_' }}externals: ['lodash', 'jquery']externals: 'lodash'具体请参考官网externals ...

May 15, 2019 · 5 min · jiezi

webpack4学习笔记二

前言这是我花了几个星期学习webpack4的学习笔记。内容不够细,因为一些相对比较简单的,就随意带过了。希望文章能给大家带来帮助。如有错误,希望及时指出。例子都在learn-webpack仓库上。如果你从中有所收获的话,希望你能给我的github点个star。 tree shaking一个模块里会导出很多东西。把一个模块里没有被用到的东西都给去掉。不会把他打包到入口文件里。tree shaking只支持es6的方式引入(import),使用require无法使用tree shaking。 webpack的development无法使用tree shaking功能。除非在打包的配置里加上 // 开发环境需要加如下代码optimization: { usedExports: true}当你需要import某个模块,又不想tree shaking把他给干掉,就需要在package.json里修改sideEffects参数。比如当你import './console.js' , import './index.css'等没有export(导出)模块的文件。又不想tree shaking把它干掉。 // package.jsonsideEffects: ['./console.js', './index.css']// 反之sideEffects: false在development环境即使你使用tree shaking,它也不会把其他多余的代码给干掉。他只会在打包的文件里注明某段代码是不被使用的。 development 和 production 区别development代码不压缩,production代码会压缩 省略…☺ webpack-merge react和vue都会区分环境进行不同的webpack配置,但是它们一定会有相同的部分。这个时候需要通过使用webpack-merge进行抽离。 // webpack.base.config.jsconst path = require('path')const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = { mode: 'production', // mode: 'development', entry: './index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] }, optimization: { usedExports: true }, plugins: [ new HtmlWebpackPlugin({ template: './index.html' }) ]}// webpack.dev.config.jsconst merge = require('webpack-merge')const baseConfig = require('./webpack.base.config')const devConfig = { mode: 'development',}module.exports = merge(baseConfig, devConfig)这里就不重复把production环境在配置出来了,主要介绍下webpack-merge用法。 ...

May 15, 2019 · 3 min · jiezi

webpack4学习笔记四

前言这是我花了几个星期学习webpack4的学习笔记。内容不够细,因为一些相对比较简单的,就随意带过了。希望文章能给大家带来帮助。如有错误,希望及时指出。例子都在learn-webpack仓库上。如果你从中有所收获的话,希望你能给我的github点个star。 编写loader// index.jsconsole.log('hello, atie')配置webpack.config.js // webpack.config.jsmodule: { rules: [ { test: /\.js$/, include: /src/, loader: path.resolve(__dirname, './loaders/replaceLoader.js') } ]},// 函数不能使用箭头函数module.exports = function(source) { console.log(source, 'source') return source.replace('atie', 'world')}loader文件其实就是导出一个函数,source就是webpack打包出的js字符串。这里的loader就是将上面的console.log('hello, atie')替换为console.log('hello, world') 打包下代码,不出所料。控制台就会打印出hello, world 当你想要给loader传参时,可配置如下 module: { rules: [ { test: /\.js$/, include: /src/, use: [{ loader: path.resolve(__dirname, './loaders/replaceLoader.js'), options: { name: 'haha' } }] } ]},通过给loader添加options 这样loader中就可以通过this.query获取该参数了 module.exports = function(source) { // 控制台输出:console.log('hello atie') { name: 'haha' } source console.log(source, this.query, 'source') return source.replace('atie', 'world')}当然变量不一定非要通过this.query来获取 ...

May 15, 2019 · 2 min · jiezi

和我一起学习Webpack40一-初识Webpack

一、Webpack是什么?Webpack is a module bundler. It packs CommonJs/AMD modules i. e. for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand.这一段是官网的介绍,翻译过来就是Webpack是一个模块打包工具。它可以打包CommonJs/AMD等浏览器不能直接支持的模块化标准。它可以让你把代码拆分成多个包,可实现按需加载。 二、环境搭建1.node.js和npm这个没什么多说的了,官网下载安装node.js即可,同时npm也会自动装好。 2.npm初始化npm init初始化过程中会让你填许多信息,不想填的话直接在命令后面加上-y会自动生成package.json文件。 3.安装webpackwebpack有两种安装方法,一个是全局安装,一个是本地安装。官方推荐本地安装,实际上本地安装是比较合理的,因为如果两个不同的项目用到的webpack版本不同的话,全局安装无法满足需求。 npm install webpack webpack-cli -Dwebpack-cli的作用是让我们可以在命令行中正确的使用webpack这个命令。 如果想查看本地项目的webpack版本: npx webpack -vnpx这个命令会帮我们在当前项目的node_modules目录下去找webpack。 三、简单示例首先我在npm init后的目录中创建3个文件: index.htmlindex.jsmodule.js// index.jsimport a from './module'console.log(a)// module.jsconst a = 123export default a在index.html引入index.js,然后用浏览器打开,发现报错了。这是因为浏览器无法直接识别ES6 module的语法。 此时我们使用命令: npx webpack index.js会发现根目录下生成了一个dist文件夹,里面有一个main.js文件,在index.html引入这个main.js后再打开浏览器,发现控制台打印了123。 这样我们就看到了webpack的作用,webpack可以帮我们打包浏览器无法识别的模块化语法,让我们可以把代码分割成一个一个的包的形式。 四、配置文件简单介绍在简单示例中我们没有配置任何东西webpack也打包成功了,这是因为webpack有默认的打包配置,当项目变得非常复杂的时候,我们需要许多配置项来帮助我们,默认的配置文件名为webpack.config.js,在根目录下创建: const path = require('path');module.exports = { entry: './index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }};这样配置以后使用npx webpack就可以让webpack按照自己期望的样子打包了。 ...

May 5, 2019 · 1 min · jiezi

一行一行手敲webpack4配置

一、webpack4--基本配置这一部分通过webpack的基本配置,使用loader对图片和样式进行打包,从而了解webpack4简单的用方法,保证自己能够配置正确,提升学习动力。 1.初始化配置mkdir webpack4cd webpack4mkdir demo1cd demo1npm init -y 或 npm init目录结构webpack4├── webpack4/demo1│   └── webpack4/demo1/package.json└── webpack4/README.md安装webpacknpm install webpack --save-dev 安装指定版本webapcknpm install --save-dev webpack@<version> webpack 4+ 版本,还需要安装webpack-clinpm install webpack-cli --save-dev npx webpack -v:查看webpack版本npx webpack-cli -v:查看webpack-cli版本推荐本地安装webpack和webpack-cli写这篇博客的时候webpack最新版本为:4.30.0,也是这篇学习webpack4使用的版本在demo1目录下新建src目录,在src目录下新建index.js mkdir src cd srctouch index.jsdemo1目录结构 demo1├── demo1/package.json├── demo1/package-lock.json└── demo1/src └── demo1/src/index.js在index.js中加写代码,例如: //index.jslet demo='webpack4'console.log(demo)webpack4可以零配置打包,webpack4会默认对src目录下的index.js文件打包。现在运行npx webapck,可以在demo1目录下看到dist目录,dist目录下有一个main.js文件,这就是打包后的文件,打开查找可以看到 console.log(demo),说明index.js被打包到main.js中。 2.webpack4的简单配置在demo1目录下新建webpack配置文件webpack.config.js 配置webpack--webpack.config.jsconst path = require('path')module.exports={ //mode development: 开发环境 production:生产环境 mode: 'development', //entry 入口文件配置 entry: { index: './src/index.js' }, //打包完成后文件输出位置配置 output: { //filename 设置打包后文件的名字 //如果不设置filename,则文件的名字跟入口文件路径的属性名一样 filename: 'bundle.js', //path 设置打包完成后文件输出路径 path: path.resolve(__dirname,'dist') }}运行npx webpack命令npx webpack等价于npx webpack --config webpack.config.js ...

April 26, 2019 · 5 min · jiezi

手把手教你写一个-Webpack-Loader

本文不会介绍loader的一些使用方法,不熟悉的同学请自行查看Webpack loader1、背景首先我们来看一下为什么需要loader,以及他能干什么?webpack 只能理解 JavaScript 和 JSON 文件。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。 本质上来说,loader 就是一个 node 模块,这很符合 webpack 中「万物皆模块」的思路。既然是 node 模块,那就一定会导出点什么。在 webpack 的定义中,loader 导出一个函数,loader 会在转换源模块resource的时候调用该函数。在这个函数内部,我们可以通过传入 this 上下文给 Loader API 来使用它们。最终装换成可以直接引用的模块。 2、xml-Loader 实现前面我们已经知道,由于 Webpack 是运行在 Node.js 之上的,一个 Loader 其实就是一个 Node.js 模块,这个模块需要导出一个函数。 这个导出的函数的工作就是获得处理前的原内容,对原内容执行处理后,返回处理后的内容。一个简单的loader源码如下 module.exports = function(source) { // source 为 compiler 传递给 Loader 的一个文件的原内容 // 该函数需要返回处理后的内容,这里简单起见,直接把原内容返回了,相当于该 Loader 没有做任何转换 return source;};由于 Loader 运行在 Node.js 中,你可以调用任何 Node.js 自带的 API,或者安装第三方模块进行调用: const xml2js = require('xml2js');const parser = new xml2js.Parser();module.exports = function(source) { this.cacheable && this.cacheable(); const self = this; parser.parseString(source, function (err, result) { self.callback(err, !err && "module.exports = " + JSON.stringify(result)); });};这里我们事简单实现一个xml-loader; ...

April 25, 2019 · 3 min · jiezi

webpack4.0入门学习笔记(一)

初始化项目创建项目mkdir webpack4-democd webpack4-demo npm init -y安装npm install webpack --save-dev 安装指定版本npm install --save-dev webpack@<version> webpack 4+ 版本,还需要安装webpack-clinpm install webpack-cli --save-dev 建议本地安装webpack和webpack-cli目前webpack最新版本为:4.30.0,也是本文学习webpack使用的版本项目目录结构 执行npx webpack index.js命令,生成dist目录,dist目录下是对index.js打包后得到的文件,默认是main.js文件。 webpack4的简单配置entry和output配置webpack.config.js文件const path = require('path')module.exports={ mode: 'development', //development: 开发环境 production:生产环境 //入口文件配置 //entry: './src/index.js', //等价于 /*entry: { main: './src/index.js' },*/ entry: { index: './src/index.js' }, //打包完成后文件存放位置配置 output: { //filename 设置打包后文件的名字 //如果不设置filename,则文件的名字跟入口文件路径的属性名一样 filename: 'bundle.js', //path 设置打包完成后文件存放路径 path: path.resolve(__dirname,'dist') }}在项目根目录下新建src文件夹,在src文件夹下新建index.js(入口文件)文件 执行npx webpack命令 npx webpack等价于npx webpack --config webpack.config.js ...

April 21, 2019 · 2 min · jiezi

【webpack4】不同的导出类型

webpack最基本的配置就是导出一个静态的对象,但是由于我们业务比较复杂,往往需要动态配置webpack以构建目标代码。幸运的是,webpack为我们提供了动态配置webpack文件的支持。下面介绍一下webpack的多种配置类型。1、导出为一个对象(Object)webpack最常见的配置类型为导出一个对象,即const path = require(‘path’)module.exports = { entry: ‘./src/index.js’, output: { filename: ‘bundle.js’, path: path.resolve(__dirname, ‘dist’) }}2、导出为一个函数(Function)很明显,导出为一个对象缺乏灵活性。若我们想要根据不同的开发环境构建不一样的代码。此时,webpack配置也应该随着改变。所以,webpack支持我们导出一个函数,这个导出函数的默认参数为env和argv。其中,env为环境对象,我们可以通过命令行对其进行配置(webpack可自动读取该配置),argv为命令行输入参数的map。(1)–env 参数支持各种各样的配置:InvocationResulting environmentnoteswebpack –env prod"prod"env为String类型,赋值为prodwebpack –env.prod{ prod: true }env为对象,prod是对象的属性, 默认为truewebpack –env.prod=1{ prod: 1 }env为对象,prod是对象的属性,赋值为1webpack –env.prod=foo{ prod: “foo” }env为对象,prod是对象的属性,赋值为foowebpack –env.prod –env.min[ prod: true, min: true ]env为对象,prod和min是对象的属性,默认为true。–env命令可以多次使用,不会被覆盖webpack –env.prod –env min[{ prod: true }, “min”]env 为数组,第一个数组元素为对象,prod是其属性,默认为true。第二个数组元素为String,赋值为minwebpack –env.prod=foo –env.prod=bar{ prod: [“foo”, “bar”] }env为对象,prod是对象的属性。prod是一个数组,第一个数组元素为"foo",第二个数组对象为"bar"。注意,即使这种情况下,prod值不会被覆盖,而是转化为数组。(2)argv为webpack命令行参数的map。例如,我们在命令行键入webpack –entry-filename=index,则在webpack配置文件中,我们可以通过argv[“entry-filename”]来获取命令行配置的值。即:argv[“entry-filename”] === “index”(3)举个例子:const path = require(‘path’)module.exports = function (env, argv) { return { entry: ‘./src/’ + argv[’entry-filename’] + ‘.js’, output: { filename: ‘bundle.js’, path: path.resolve(__dirname, ‘dist’), }, devtool: env.prod ? ‘source-map’ : ’eval’ }}在命令行中,我们需要自键入webpack –env.prod –entry-filename=index注意,凡是webpack配置文件中使用到的命令行参数,例如argv[“entry-filename”],均需要在命令行传入,一个不能遗漏,否则webpack会报错:Config did not export an object3、导出为一个Promise对象除了导出为一个函数,webpack还支持我们异步获取配置变量来配置相关文件。(真是很强大!)为了体现异步获取数据的过程,我们把webpack.config.js修改为以下内容:const path = require(‘path’)module.exports = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ entry: ‘./src/index.js’, output: { filename: ‘bundle.js’, path: path.resolve(__dirname, ‘dist’) } }) }, 1000) })}4、导出多个配置修改webpack.config.js的内容如下:const path = require(‘path’)module.exports = [ { name: ‘index’, entry: ‘./src/index.js’, output: { filename: ‘index.js’, path: path.resolve(__dirname, ‘dist’) }, mode: ‘production’ }, { name: ‘main’, entry: ‘./src/main.js’, output: { filename: ‘main.js’, path: path.resolve(__dirname, ‘dist’) } }]当我们运行webpack时,以上多个配置都会被构建出来。如果我们只想构建其中一份代码,那么只需要传入–config-name参数即可:webpack –config-name=indexwebpack –config-name=main如上所示,我们可以分别构建出name为index、name为main的配置对象所配置的内容。以上配置常见的场景有:npm包利用不同的模块语法构建不同的输出文件,供不同的项目进行使用:module.exports = [{ output: { filename: ‘./dist-amd.js’, libraryTarget: ‘amd’ }, name: ‘amd’, entry: ‘./app.js’, mode: ‘production’,},{ output: { filename: ‘./dist-commonjs.js’, libraryTarget: ‘commonjs’ }, name: ‘commonjs’, entry: ‘./app.js’, mode: ‘production’,}]总而言之,webpack为我们提供了众多灵活的配置解决方案。当我们遇到复杂项目的时候,只要明确心中需求,大多数都能够找到对应的解决方案。 ...

April 8, 2019 · 1 min · jiezi

webpack4升级全纪录

webpack4的发布已经有好长一段时间了,目前也有了相对稳定的版本。是时候开始升级了在此记录一下整个升级的过程,遇到的问题,和相应解决的方案,也方便自己之后的复盘反思,以及后续入坑同学们,避免踩坑,或者是一些思路。能提供到一点点帮助,也是笔者的荣幸。欢迎点赞支持ps:如有错误,也望指出,多多包涵。如果同学们有更好的思路或者解决方案,也欢迎分享学习~~????一、升级相应node版本官方更新日志中已经有了详细的说明,因为前些日子做其他项目的时候涉及到了node的升级,所以在此略过。具体的升级方式,可以看这篇Linux环境升级Node版本二、升级webpack4在查阅了一些其他前辈们的经验后,开始尝试升级自己项目webpack4根据错误对webpack-dev-server进行版本升级webpack4三、总结未完待续。。

March 26, 2019 · 1 min · jiezi

vue-pdf在打包时报错解决方案

if (Object({"NODE_ENV":"production","API_ROOT":http://192.168.6.112:8080/MaxTunnel-Web}).VUE_ENV !== 'server' {var pdfjsWrapper = __webpack_require__("ytml").default; Build failed with errors. 打包时报这个错误是因为传入的src地址,没有解析成字符串,修改办法是在build/webpack.prod.conf.js和build/webpack.dev.conf.js中,加入 new webpack.DefinePlugin({ ‘process.env’: env, ‘process.env.VUE_ENV’: JSON.stringify(process.env.VUE_ENV) //增加此行,把传入的src地址转成json字符串的格式 }),在node_modules的vue-pdf中,找到vuePdfSss.vue中,找到if ( process.env.VUE_ENV !== ‘server’ )这句话,问题就出在这。

March 18, 2019 · 1 min · jiezi

从零开始的Webpack4教程

1、了解Webpack相关什么是webpackWebpack是一个模块打包器(bundler)。在Webpack看来, 前端的所有资源文件(js/json/css/img/less/…)都会作为模块处理它将根据模块的依赖关系进行静态分析,生成对应的静态资源五个核心概念Entry:入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。Output:output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。Loader:loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只能解析 JavaScript)。Plugins:插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。Mode:模式,有生产模式production和开发模式development理解LoaderWebpack 本身只能加载JS/JSON模块,如果要加载其他类型的文件(模块),就需要使用对应的loader 进行转换/加载Loader 本身也是运行在 node.js 环境中的 JavaScript 模块它本身是一个函数,接受源文件作为参数,返回转换的结果loader 一般以 xxx-loader 的方式命名,xxx 代表了这个 loader 要做的转换功能,比如 json-loader。理解Plugins插件可以完成一些loader不能完成的功能。插件的使用一般是在 webpack 的配置信息 plugins 选项中指定。配置文件(默认)webpack.config.js : 是一个node模块,返回一个 json 格式的配置信息对象2、开启项目初始化项目:生成package.json文件{ “name”: “webpack_test”, “version”: “1.0.0”} 安装webpacknpm install webpack webpack-cli -g //全局安装npm install webpack webpack-cli -D //本地安装3、编译打包应用创建js文件src/js/app.jssrc/js/module1.jssrc/js/module2.jssrc/js/module3.js创建json文件src/json/data.json创建主页面:src/index.html运行指令开发配置指令:webpack src/js/app.js -o dist/js/app.js –mode=development功能: webpack能够编译打包js和json文件,并且能将es6的模块化语法转换成浏览器能识别的语法生产配置指令:webpack src/js/app.js -o dist/js/app.js –mode=production功能: 在开发配置功能上加上一个压缩代码结论:webpack能够编译打包js和json文件能将es6的模块化语法转换成浏览器能识别的语法能压缩代码缺点:不能编译打包css、img等文件不能将js的es6基本语法转化为es5以下语法改善:使用webpack配置文件解决,自定义功能4、使用webpack配置文件目的:在项目根目录定义配置文件,通过自定义配置文件,还原以上功能文件名称:webpack.config.js文件内容:const {resolve} = require(‘path’); //node内置核心模块,用来设置路径。module.exports = { entry: ‘./src/js/app.js’, // 入口文件 output: { // 输出配置 filename: ‘./js/bundle.js’, // 输出文件名 path: resolve(__dirname, ‘dist’) //输出文件路径配置 }, mode: ‘development’ //开发环境(二选一) mode: ‘production’ //生产环境(二选一)};运行指令: webpack5、js语法检查安装loadernpm install eslint-loader eslint –save-dev配置loadermodule: { rules: [ { test: /.js$/, //只检测js文件 exclude: /node_modules/, //排除node_modules文件夹 enforce: “pre”, //提前加载使用 use: { //使用eslint-loader解析 loader: “eslint-loader” } } ]}修改package.json(需要删除注释才能生效)“eslintConfig”: { //eslint配置 “parserOptions”: { “ecmaVersion”: 8, // es8 “sourceType”: “module”, // ECMAScript 模块 }}运行指令:webpack6、js语法转换安装loadernpm install babel-loader @babel/core @babel/preset-env –save-dev配置loadermodule: { rules: [ { oneOf: [ //数组中的配置只有一个能够生效, 后面的配置都会放在当前数组中 { test: /.js$/, exclude: /node_modules/, use: { loader: “babel-loader”, options: { presets: [’@babel/preset-env’] } } } ] } ]}运行指令:webpack7、打包less资源创建less文件src/less/test1.lesssrc/less/test2.less入口app.js文件引入less资源安装loadernpm install css-loader style-loader less-loader less –save-dev配置loaderoneOf: [ { test: /.less$/, use: [{ loader: “style-loader” }, { loader: “css-loader” }, { loader: “less-loader” }] }]运行指令:webpack在index.html中引入打包生成的dist/js/bundle.js文件,观察效果8、打包样式文件中的图片资源添加2张图片:小图, 小于8kb: src/images/1.png大图, 大于8kb: src/images/2.jpg在less文件中通过背景图的方式引入图片安装loadernpm install file-loader url-loader –save-dev补充:url-loader是对象file-loader的上层封装,使用时需配合file-loader使用。配置loader{ test: /.(png|jpg|gif|svg)$/, use: [ { loader: ‘url-loader’, options: { outputPath: ‘images/’, //在output基础上,修改输出图片文件的位置 publicPath: ‘../dist/images/’, //修改背景图引入url的路径 limit: 8 * 1024, // 8kb大小以下的图片文件都用base64处理 name: ‘[hash:8].[ext]’ // hash值为7位,ext自动补全文件扩展名 } } ]}运行指令:webpack在index.html中引入打包生成的dist/js/bundle.js文件,观察效果9、打包html文件添加html文件src/index.html注意不要在html中引入任何css和js文件安装插件Pluginsnpm install clean-webpack-plugin –save-dev在webpack.config.js中引入插件(插件都需要手动引入,而loader会自动加载)const CleanWebpackPlugin = require(‘clean-webpack-plugin’)配置插件Pluginsplugins: [ new HtmlWebpackPlugin({ template: ‘./src/index.html’ }),]运行指令:webpack10、打包html中图片资源添加图片在src/index.html添加两个img标签安装loadernpm install html-loader –save-dev修改entryentry: [’./src/js/app.js’, ‘./src/index.html’]配置loader{ test: /.(html)$/, use: { loader: ‘html-loader’ }}运行指令:webpack11、打包其他资源添加字体文件src/media/iconfont.eotsrc/media/iconfont.svgsrc/media/iconfont.ttfsrc/media/iconfont.woffsrc/media/iconfont.woff2修改样式@font-face { font-family: ‘iconfont’; src: url(’../media/iconfont.eot’); src: url(’../media/iconfont.eot?#iefix’) format(’embedded-opentype’), url(’../media/iconfont.woff2’) format(‘woff2’), url(’../media/iconfont.woff’) format(‘woff’), url(’../media/iconfont.ttf’) format(’truetype’), url(’../media/iconfont.svg#iconfont’) format(‘svg’);}.iconfont { font-family: “iconfont” !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;}修改html,添加字体配置loader{ loader: ‘file-loader’, exclude: [/.js$/, /.html$/, /.json$/], options: { outputPath: ‘media/’, publicPath: ‘../dist/media/’, name: ‘[hash:8].[ext]’, },}运行指令:webpack12、自动编译打包运行安装loadernpm install webpack-dev-server –save-dev引入webpackconst webpack = require(‘webpack’);修改webpack配置对象(注意不是loader中)devtool: ‘inline-source-map’, // 将编译后的代码映射回原始源代码,更容易地追踪错误和警告devServer: { contentBase: ‘./dist’, //项目根路径 hot: true, //开启热模替换功能 open: true //自动打开浏览器},plugins: [ new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin()]修改loader部分配置因为构建工具以dist为根目录,不用再找dist了publicPath: ‘../dist/images/’ –> publicPath: ‘images/‘publicPath: ‘../dist/media/’ –> publicPath: ‘media/‘修改package.json中scripts指令"start": “webpack-dev-server”,“dev”: “webpack-dev-server"运行指令:npm start / npm run dev以上就是webpack开发环境的配置,可以在内存中自动打包所有类型文件并有自动编译运行、热更新等功能。13、准备生产环境创建文件夹config,将webpack.config.js复制两份webpack.dev.jswebpack.prod.js修改webpack.prod.js配置,删除webpack-dev-server配置module.exports = { output: { filename: ‘js/[name].[hash:8].js’, //添加了hash值, 实现静态资源的长期缓存 publicPath: ‘/’ //所有输出资源公共路径 }, module: { //loader其他没有变化,只放了变化部分,只有需要修改路径部分改了 rules: [ { oneOf: [ { test: /.(png|jpg|gif|svg)$/, use: [ { loader: ‘url-loader’, options: { limit: 8 * 1024, // 8kb大小以下的图片文件都用base64处理 name: ‘images/[name].[hash:8].[ext]’ } } ] }, { loader: ‘file-loader’, exclude: [/.js$/, /.html$/, /.json$/], options: { name: ‘media/[name].[hash:8].[ext]’, }, } ] } ] }, mode: ‘production’, //修改为生产环境}修改package.json的指令"start”: “webpack-dev-server –config ./config/webpack.dev.js"“dev”: “webpack-dev-server –config ./config/webpack.dev.js"“build”: “webpack –config ./config/webpack.prod.js"开发环境指令npm startnpm run dev生产环境指令npm run build注意: 生产环境代码需要部署到服务器上才能运行npm i serve -gserve -s dist14、清除打包文件目录安装插件npm install clean-webpack-plugin –save-dev引入插件const CleanWebpackPlugin = require(‘clean-webpack-plugin’);配置插件new CleanWebpackPlugin()运行指令:npm run build15、提取css成单独文件安装插件npm install mini-css-extract-plugin –save-dev引入插件const MiniCssExtractPlugin = require(“mini-css-extract-plugin”);配置loader{ test: /.less$/, use: [ MiniCssExtractPlugin.loader, ‘css-loader’, ’less-loader’, ]}配置插件new MiniCssExtractPlugin({ filename: “css/[name].[hash:8].css”, chunkFilename: “css/[id].[hash:8].css”})运行指令npm run buildserve -s dist16、添加css兼容安装loadernpm install postcss-loader autoprefixer –save-dev配置loader{ test: /.less$/, use: [ MiniCssExtractPlugin.loader, ‘css-loader’, ‘postcss-loader’, ’less-loader’, ]}在项目根目录添加postcss配置文件:postcss.config.jsmodule.exports = { “plugins”: { “autoprefixer”: { “browsers”: [ “ie >= 8”, “ff >= 30”, “chrome >= 34”, “safari >= 8”, “opera >= 23” ] } }}运行指令:npm run buildserve -s dist17、压缩css安装插件npm install optimize-css-assets-webpack-plugin –save-dev引入插件const OptimizeCssAssetsPlugin = require(‘optimize-css-assets-webpack-plugin’);配置插件new OptimizeCssAssetsPlugin({ cssProcessorPluginOptions: { preset: [‘default’, { discardComments: { removeAll: true } }], },})运行指令:npm run buildserve -s dist18、图片压缩安装loadernpm install img-loader imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo imagemin –save-dev配置loader{ test: /.(png|jpg|gif|svg)$/, use: [ { loader: ‘url-loader’, options: { limit: 8 * 1024, // 8kb大小以下的图片文件都用base64处理 name: ‘images/[name].[hash:8].[ext]’ } }, { loader: ‘img-loader’, options: { plugins: [ require(‘imagemin-gifsicle’)({ interlaced: false }), require(‘imagemin-mozjpeg’)({ progressive: true, arithmetic: false }), require(‘imagemin-pngquant’)({ floyd: 0.5, speed: 2 }), require(‘imagemin-svgo’)({ plugins: [ { removeTitle: true }, { convertPathData: false } ] }) ] } } ]}运行指令:npm run buildserve -s dist19、压缩html修改插件配置new HtmlWebpackPlugin({ template: ‘./src/index.html’, minify: { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true, }})运行指令:npm run buildserve -s dist以上就是webpack生产环境的配置,可以生成打包后的文件。到这里基本配置已经告一段落了,所有配置我已经放在 仓库 中了。后期会考虑出一期优化配置,将webpack优化到底~ ...

March 17, 2019 · 3 min · jiezi

简易脚手架搭建

写在最前面随着公司业务的发展,独立的系统也慢慢多了起来,脚手架的必要性也日趋明显。基于此,便开始搭起了脚手架,主要解决两个问题:1.项目的反复配置2.公共组件的自动更新代码地址点这里,可以照着代码再去看这篇文章会觉得更有思路一点使用1.全局按装npm install zn-cli -g2.创建实例zn-cli init [templateType] [projectName]ps:templateType: 模板类型(暂时支持Vue和react的标准模板,后期会陆续支持后台版本,类似于antdPro)projectName: 你需要创建的项目实例名称3.更新脚手架中的公共文件在你的项目实例中执行:npm run update安全性:首先,这个操作是不会覆盖你的业务代码,只会去更新根目录下public文件夹里的文件可选性:每个版本更新的公共文件都会在脚手架的文档中说明,你可以选择你需要的功能对应的版本(后续再讲)代码1.分支master:脚手架代码,全局安装的就是这个玩意,用来拉去对应模板生成react/vue实例的,通过npm安装使用template分支:分支名template开头的都是模板分支。我这里就不一一列举了,因为分支会陆续的添加,脚手架里都会说明test分支:测试分支,用来测试一些功能或者模板用的(可忽略)2.脚手架实现核心代码是在bin目录里,index.js为入口,点开index,可以看到,主要是对node版本做了判断,然后底部引入了cli.js,在看这些代码之前,首先得闲确定你是对以下几个node包是了解的:commander:处理用户命令行输入download:clone git远程仓库代码其他有一些包是辅助类的,比如chalk,ora等,不了解也没关系。先看代码:大致流程:1.获取用户输入2.根据用户输入判断是需要什么模板,下载对应模板到用户输入的项目目录中3.模板下载完成后,调用_cli下面的_reWritePackageJson方法,去更改实例中package.json中的一些配置4.log里面的东西是一些提示性信息基本脚手架就是这个流程,根据用户输入去拉去对应的模板代码到指定的目录。可能你注意到在模板下载完成时我执行了一个进程:spawn(‘rm’, [’-rf’, ${name}/build.js]);删除创建的项目中的build.js文件,为什么,因为这个js是用来更新模板,就像上面我说到的执行npm run update的时候就是通过这个js去做处理的。显然,他应该只存在于脚手架的模板中,而不应该出现在用户的项目实例中。对于用户而言,它是无用的。总结以上就是一个简单脚手架的实现方式,后期也会一直去更新和维护这个脚手架,公司现在新的项目就是基于这个脚手架去搭建的,因为公司现在的项目都是react写的,所以Vue的功能暂时不支持,后期也会慢慢支持vue…

March 14, 2019 · 1 min · jiezi

造轮子--webpack4&vue2

初始化我们使用npm init命令初始化一个package.json文件。npm init安装webpacknpm i webpack webpack-cli webpack-dev-server –save-dev安装vuenpm i vue –save创建相应文件createVue |–dist |–webpack.config.js |–src |–main.js |–index.html===index.html<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>webpack4-vue-demo</title></head><body> <div id=“app”></div> <script src="./dist/bundle.js"></script></body></html>====main.jsimport Vue from ‘vue’;new Vue({ el: ‘#app’, data: { message: “hello” }}); ===webpack.config.jsconst webpack = require(‘webpack’);module.exports = { entry: ‘./src/main.js’, //入口 output:{ path: path.resolve(__dirname, ‘dist’), filename: “bundle.js” }, resolve: { extensions: [’.js’, ‘.vue’, ‘.json’], alias: { ‘@’: resolve(‘src’) } }};打包webpack打包过后,dist文件夹出现打包后的文件,再通过IDEA打开index.html,就可以看到内容。打开index.html后,控制台报错[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.解决方法: resolve: { alias: { ‘vue$’: ‘vue/dist/vue.esm.js’ } }改造成vue文件createVue |–dist |–webpack.config.js |–src |–main.js |–App.vue |–index.html====main.jsimport Vue from ‘vue’;import App from ‘./APP’;new Vue({ el: ‘#app’, render:h=>h(App)}); ====App.vue<template> <div id=“app”> hello world </div></template><script>export default { name: ‘app’}</script> 如果直接运行,发现报错。解决方案:npm install -D vue-loader vue-template-compiler请在webpack.config.js添加如下:const VueLoaderPlugin = require(‘vue-loader/lib/plugin’)module.exports = { module: { rules: [ // … 其它规则 { test: /.vue$/, loader: ‘vue-loader’ } ] }, plugins: [ // 请确保引入这个插件! new VueLoaderPlugin() ]}改造webpack.config.jsnpm i webpack-merge –save-dev新建两个文件,webpack.prod.js和webpack.dev.js。createVue |–dist |–webpack.config.js |–webpack.dev.js |–webpack.prod.js |–src |–main.js |–App.vue |–index.html======= webpack.dev.jsconst merge = require(‘webpack-merge’);const common = require(’./webpack.config.js’);const path = require(‘path’);module.exports = merge(common, { devtool: ‘inline-source-map’, devServer: { // 开发服务器 contentBase: ‘./dist’ //告诉开发服务器(dev server),在哪里查找文件 }, output: { // 输出 filename: ‘js/[name].[hash].js’, // 每次保存 hash 都变化 path: path.resolve(__dirname, ‘./dist’) }, module: {}, mode: ‘development’,});======= webpack.prod.jsconst path = require(‘path’);// 合并配置文件const merge = require(‘webpack-merge’);const common = require(’./webpack.config.js’);module.exports = merge(common, { module: {}, plugins: [], mode: ‘production’, output: { filename: ‘js/[name].[contenthash].js’, //contenthash 若文件内容无变化,则contenthash 名称不变 path: path.resolve(__dirname, ‘./dist’) },});接下来,在package.json的scripts 配置中添加运行脚本:“scripts”: { “start”: “webpack-dev-server –open –config webpack.dev.js”, “build”: “webpack –config webpack.prod.js”},当我们打包的时候webpack4会报错The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.You can also set it to ’none’ to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/解决方法:“scripts”: { “start”: “webpack-dev-server –mode=‘development’ –open –config webpack.dev.js”, “build”: “webpack –mode=‘production’ –config webpack.prod.js”},热替换在webpack.dev.js 中增加如下配置:devServer: { hot: true},plugins: [ new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin()],压缩jsnpm i -D uglifyjs-webpack-plugin请在webpack.prod.js添加如下配置:const UglifyJsPlugin = require(‘uglifyjs-webpack-plugin’)module.exports = { plugins: [ new UglifyJsPlugin() ]}清理 /dist 文件夹npm install clean-webpack-plugin –save-dev请在webpack.prod.js添加如下配置:const UglifyJsPlugin = require(‘uglifyjs-webpack-plugin’)module.exports = { plugins: [ new CleanWebpackPlugin([‘dist’]) ]}引入babel复制代码由于在使用vue时会用到很多es6的语法,但是现在很多浏览器对es6的支持不是很好,所以在编译时需要将这些语法转换es5的语法,此时我们使用babel来进行编译。npm install –save-dev babel-core babel-loader babel-preset-env babel-plugin-transform-runtimebabel-loader 用于让 webpack 知道如何运行 babelbabel-core 可以看做编译器,这个库知道如何解析代码babel-preset-env 这个库可以根据环境的不同转换代码请在webpack.config.js添加如下配置:module:{ rules:[ { test: /.js$/, loader:“babel-loader”, exclude: /node_modules/ } ]}目录下创建.babelrc文件用于配置 babel :{ “presets”: [“env”], “plugins”: ["@babel/plugin-transform-runtime"]}HtmlWebpackPlugin 插件将打包后的文件自动注入index.html。npm install –save-dev html-webpack-plugin将index.html页面中的"<script src="./dist/bundle.js"></script>" 删除。在webpack.config.js 中增加如下配置:const HtmlWebpackPlugin = require(‘html-webpack-plugin’)…plugins:[ new HtmlWebpackPlugin()]运行"npm build", 生成的index.html中自动注入打包好的js文件。引入vue-routernpm install –save vue-router添加 src/router/routes.js 文件,用于配置项目路由:import Vue from ‘vue’;import Router from ‘vue-router’;Vue.use(Router);export default new Router({ routes: [ { path: ‘’, component: () => import (’@/views/Home’)} ]});新创建文件如下:createVue |–dist |–webpack.config.js |–webpack.dev.js |–webpack.prod.js |–src |–views |–Home.vue |–router |–routes.js |–main.js |–App.vue |–index.html运行npm start,发现报错了!!!注意如果您使用的是 Babel,你将需要添加 syntax-dynamic-import 插件,才能使 Babel 可以正确地解析语法。找了好久,才发现,原来还需要装这个插件~~~哭.jpgnpm install –save-dev @babel/plugin-syntax-dynamic-import// .babelrc{ “plugins”: ["@babel/plugin-syntax-dynamic-import"]}到这儿,完成了基本的框架,剩下的可以看自己的项目需要进行选择了。 ...

March 11, 2019 · 3 min · jiezi

初识webpack,搭建webpack4+typescript+scss入门项目

为什要使用webPack随着大前端的来临,需要前端处理越来越多的事情,不仅局限页面的交互,项目的需求越来越多,更多的逻辑需要在前端完成,为了简化开发的复杂度,前端社区涌现出了很多好的实践方法。1.模块化,一种将复杂系统分解为可管理模块的方式,简单来说就是解耦,简化开发,一个模块就是实现特定功能的文件,想要什么功能,就加载什么模块,完美的实现了代码重用。其中三大剑客便是Angular,React和Vue2.JavaScript的拓展的开发语言typescript,能够实现目前版本的JavaScript不能直接使用的特性,并且之后还能转换为浏览器可识别的javascript语言3.scss less等CSS预处理器 ….这些改进确实大大的提高了我们的开发效率,但是利用它们开发的文件往往需要进行额外的处理才能让浏览器识别,而手动处理又是非常繁琐的,这就为webPack类的工具的出现提供了需求。webpack是什么webpack是一个模块打包工具,它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分割,等到实际需要的时候再异步加载。webpack支持的模块1.ES2015 import语句2.CommonJS require()语句3.AMD define 和 require语句4.css/sass/less文件的 @import 语句5.样式 (url(…)) 或 HTML文件(<img src=…>) 中的图片链接(image url)webPack的特点1.丰富的插件,方便进行开发工作2.大量的加载器,包括加载各种静态资源3.代码分割,提供按需加载的能力4.发布工具webpack的工作方式把项目当做一个整体,通过一个给定的主文件(如:index.js),从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。开始项目搭建1.全局安装webpacknpm install -g webpack2.创建练习文件夹mkdir webpack_pratice3.创建package.json文件进入到webpack_pratice,创建package.json文件,在终端中使用npm init命令可以自动创建这个package.json文件 npm init输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果你不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。package.json是一个标准的npm说明文件,里面蕴含了丰富的信息,包括当前项目的依赖模块,自定义的脚本任务等等。4.安装项目需要模块 npm install webpack webpack-cli –save-dev npm install typescript ts-loader –save-dev npm install style-loade node-sass sass-loader css-loader –save-dev npm install extract-text-webpack-plugin@next –save-dev npm install html-webpack-plugin –save-dev注意:extract-text-webpack-plugin 最新版本为 3.0.2,这个版本还没有适应 webpack 4 的版本,所以安装的时候需要extract-text-webpack-plugin@next5.构建项目结构a.创建src目录,增加index.html main.ts common.css main.scss index.html<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>webpack测试</title></head><body> <div class=“test-block”> Webpack </div></body></html>main.tsimport ‘./main.scss’class TestMain { name: string; age: number; constructor ( name: string, age: number ) { this.name = name; this.age = age; }; testFun() { console.log(this.name + ‘—’ + this.age) }}let testMain = new TestMain(“Miss D”, 18);testMain.testFun();common.csshtml,body{ margin: 0; padding: 0; font-size: 25px; color: yellow;}main.scss@import “common.scss”;.test-block{ width: 300px; height: 300px; margin: 0 auto; background: green;}b.创建dist文件夹c.创建TypeScript的配置文件tsconfig.json{ “compilerOptions”: { “module”: “commonjs”, “target”: “es5”, “sourceMap”: true }, “exclude”: [ “node_modules” ]}d.创建webpack的配置文件webpack.config.jsconst path = require(‘path’);const htmlWebpackPlugin = require(‘html-webpack-plugin’);const ExtractTextPlugin = require(’extract-text-webpack-plugin’);module.exports = { entry: ‘./src/main.ts’, //模块的入口 output: { //输出配置 filename: ‘[name].bundle.js’, path: path.resolve(__dirname, ‘dist’) }, module: { //资源模块的处理器 rules: [ { test: /.tsx?$/, use: ’ts-loader’, exclude: path.resolve(__dirname, ’node_modules’), include: path.resolve(__dirname, ‘src’), }, { test: /.scss$/, use: ExtractTextPlugin.extract({ fallback: { loader: “style-loader” }, use: [ { loader: “css-loader”, }, { loader: “sass-loader” } ] }) }] }, plugins: [ //插件配置 new ExtractTextPlugin({ filename: “[name].min.css” }), new htmlWebpackPlugin({ template: ‘./src/index.html’, inject: ‘head’ }) ]}e.项目文件夹结构6.package.js配置build执行脚本{ “name”: “webpack-pratice”, “version”: “1.0.0”, “description”: “”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1”, “build”: “webpack –config webpack.config.js” }, “author”: “”, “license”: “ISC”, “devDependencies”: { “html-webpack-plugin”: “^3.2.0”, “css-loader”: “^2.1.0”, “extract-text-webpack-plugin”: “^4.0.0-beta.0”, “node-sass”: “^4.11.0”, “sass-loader”: “^7.1.0”, “style-loader”: “^0.23.1”, “ts-loader”: “^5.3.3”, “typescript”: “^3.3.3333”, “webpack”: “^4.29.6”, “webpack-cli”: “^3.2.3” }}7.执行打包命令npm run build打包结果7.运行index.html项目的scss样式和typescript文件,解析成浏览器能识别的css和javascript文件,且自动引入到index.html文件中。官方参考1.webpack官网 链接描述2.html-webpack-plugin插件 链接描述3.extract-text-webpack-plugin插件 链接描述踩坑如果使用webpack,style-loader出现的错误:ERROR in {project}/node_modules/style-loader/lib/addStyles.jsModule not found: Error: Can’t resolve ‘./urls’ in ‘{project}\node_modules\style-loader\lib’解决方法: 直接修改{project}/node_modules/style-loader/lib/addStyles.js文件: 把require(“./urls”)改为require(“./urls.js”) ...

March 6, 2019 · 2 min · jiezi

webpack4配置Vue多页面入口轻量级模板

前言之前写过一次关于webpack配置多页面应用,写的不是很好,这次项目要用到多页面应用,于是重新基于webpack4构建了一套关于vue的多页面应用。我在网上搜索了一圈,发现vue多页面配置,大部分都是基于vue-cli配置的,很少是从基础开始配置,如是我通过webpack4,构建了一个提供多页面入口,打包,调试的轻量级的构建工具,不依赖过多配置,只加载常用的配置,用更少的代码,做更多的东西项目结构├── build // webpack配置目录│ ├── webpack.config.base.js // 公共配置│ ├── webpak.config.dev.js // 开发模式│ ├── webpak.config.prod.js // 打包模式├── dist // 项目打包路径(自动生成)├── page // 多页面入口(自定义)├── public // index.html模板├── src // 源码目录(自定义)├── postcss.config // 样式添加前缀├── pages.js // 多页面配置项项目运行克隆项目git clone git@github.com:hangjob/vue-multiple-webpack4-template.git安装依赖npm install 或 yarn开发模式npm run dev里面已经写好了两个入口文件,启动后可直接访问http://localhost:3000/home.htmlhttp://localhost:3000/login.html打包模式npm run build打包后生成文件dist目录文件解释关于build中使用的插件项在文件配置后面注释写的都很清楚多页面配置项(pages.js)pages: [ { page: ‘home’, entry: path.resolve(__dirname, ‘./page/home.js’), //指向入口文件 title: ‘这是页面1’, template: path.resolve(__dirname, ‘./public/index.html’), //指向模板文件 filename: ‘home.html’, chunks: [‘home’,‘common’], // 引入公共模块common }, { page: ’login’, entry: path.resolve(__dirname, ‘./page/login.js’), //指向入口文件 title: ‘这是页面2’, template: path.resolve(__dirname, ‘./public/index.html’), //指向模板文件 filename: ’login.html’, chunks: [’login’], }]webpack.config.dev.js 开发模式mode: ‘development’,devtool: ‘cheap-module-eval-source-map’,// 原始代码(只有行内),但是更高的质量和更低的性能watch: true,watchOptions: { poll: 1000, //每秒监控讯问次数 aggregateTimeout: 500, //防抖 ignored: ‘/node_modules/’ //忽略监控文件 },devServer:{ port: 3000, hot: true, progress: false, //记录条 contentBase: path.resolve(__dirname, ‘../public’), //表示的是告诉服务器从哪里提供内容 compress: true //开启gzip压缩}webpack.config.prod.js 生产模式ode: ‘production’,devtool: ‘cheap-module-source-map’,// 原始代码(只有行内)每行代码从loader中进行映射plugins: [ new CleanWebpackPlugin([‘dist’], { root: path.resolve(__dirname, ‘..’), dry: false // 启用删除文件 })],optimization: { minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true, //启用缓存并且启用多进程并行运行 sourceMap: true //错误消息位置映射(减慢编译速度),线上错误方便查看 }), new OptimizeCSSAssetsPlugin({}) ]}webpack.config.base.js 公共模块optimization: { splitChunks: { cacheGroups: { // 将 node_modules目录下被打包的代码到common/common.js common: { test: /node_modules/, chunks: “initial”, //只对入口文件处理 name: “common”, //配置公共模块名称 minChunks: 2, //表示被引用次数,默认为1,比如在项目中有2处引用到一样的模块就会抽离到公共模块下 maxInitialRequests: 5, // 最大的初始化加载次数,默认为1 minSize: 0 //表示在压缩前的最小模块大小,默认为0 } } }} 网络下载太慢,请使用淘宝镜像1.临时使用npm –registry https://registry.npm.taobao.org install express2.持久使用npm config set registry https://registry.npm.taobao.org配置后可通过下面方式来验证是否成功npm config get registry 或者 npm info express3.通过cnpm使用npm install -g cnpm –registry=https://registry.npm.taobao.org说明github地址,后面会持续更新,如果对您有帮助,您可以点右上角 “Star” 支持一下 谢谢! ^_^ 如要在编译过程中遇到错误,点击联系作者感谢这些文章提供帮助项目有使用到这些文章的我都注释过webpack中的path、publicPath和contentBasemini-css-extract-pluginwebpack4 splitChunksPlugin && runtimeChunkPlugin 配置杂记Vue Loader ...

March 1, 2019 · 1 min · jiezi

超级详细的手写webpack4配置来启动vue2项目(附配置作用)

基础目录结构以及各个文件的作用初始npm项目npm init // 一路回车,一律使用默认的npm项目配置给生成的package.json 加上scripts 用来启动我们的项目 // 如下{ “name”: “doing-a-webpack4-vue2-pro”, “version”: “1.0.0”, “description”: “超级详细的手写webpack4配置来启动vue2项目(附配置作用)”, “main”: “index.js”, “author”: “”, “license”: “ISC”, “scripts”: { “dev”: “webpack-dev-server –config webpack/webpack-dev-config.js” }, “engines”: { “node”: “>= 8.0.0”, “npm”: “>= 3.0.0” }, “browserslist”: [ “> 1%”, “last 2 versions”, “not ie <= 8” ]}说明:npm run dev 用来启动命令 webpack-dev-server –config webpack/webpack.dev.config.js这里将开发环境(development)的配置 webpack/webpack-dev-config.js 传入到启动的server config中。详情故这里需要做两件事情:a. npm install webpack-dev-server -D 开发依赖b. 书写 webpack.dev.config.js书写 webpack.dev.config.js说明: 由于 webpack.dev.config.js 与 webpack.prod.config.js 近似,所以手写一个 webpack.base.config.js来减少配置耦合量。 提示:base.config与拓展的config需要用webpack提供的 webpack-merge 来合并 故这里需要做两件事情: a. npm install webpack-dev-server -D 这个放到后面安装config需要的依赖中一起做,稍后会写到 b. 书写 webpack.base.config.js书写 webpack.base.config.jsconst path = require(“path”)const { VueLoaderPlugin } = require(‘vue-loader’)const ifProd = process.env.NODE_ENV === ‘production’ ? true : falseconst config = { dev: { mode: ‘development’, assetsPublcPath: ‘/’, assetsSubDirectory: ‘./’ }, prod: { mode: ‘production’, index: path.resolve(__dirname, “../dist/index.html”), assetsPublcPath: path.resolve(__dirname, “../dist”), assetsSubDirectory: ‘./’ }}module.exports = { mode: ifProd ? ‘production’ : ‘development’, context: path.resolve(__dirname, ‘../’), entry: { app: ‘./src/main.js’ }, output: { filename: ‘[name].bulde.[hash:10].js’, path: ifProd ? config.prod.assetsPublcPath : config.dev.assetsPublcPath }, resolve: { extensions: [’.js’, ‘.vue’], }, devServer: { quiet: true }, module: { rules: [ { test: /.vue$/, use: [ { loader: ‘vue-loader’, } ] }, { test: /.js$/, exclude: /node_modules/, loader: ‘babel-loader’, options: { presets: [‘babel-preset-env’] } }, { test: /.css$/, use: [‘vue-style-loader’, ‘css-loader’] } ] }, plugins: [ new VueLoaderPlugin() ]}我们可以看到,这里base.config需要的开发依赖有:babel-loader@7 (7.x版本需要配合 babel-core babel-preset-env)webpack (4.x版本需要配合 webpack-cli)css-loader (需要配合 vue-style-loader)vue-loader (需要配合 vue-template-compiler)故在命令行执行如下命令npm install -D babel-loader@7 babel-core babel-preset-env webpack webpack-cli css-loader vue-style-loader vue-loader vue-template-compiler详细的配置说明几天后给出回到 webpack.dev.config.jsconst BaseConfig = require("./webpack.base.config")const merge = require(“webpack-merge”)const HtmlWebpackPlugin = require(‘html-webpack-plugin’)module.exports = merge(BaseConfig, { plugins: [ // https://github.com/ampedandwired/html-webpack-plugin // 这是一个webpack的插件来创建html文件渲染你的webpack生成的bundle new HtmlWebpackPlugin({ // 写入bundle的那个index.html filename: ‘index.html’, template: ‘index.html’ }) ]})我们可以看到,这里dev.config需要的开发依赖有:html-webpack-plugin故在命令行执行如下命令npm install -D html-webpack-plugin可以开始写vue啦!我们在上面的 webpack.base.config.js 中写到了 entry: {app: ‘./src/main.js’}这就是我们的vue入口了。如下:import Vue from “vue”; // 引入vueimport App from “./App”; // 引入组件Appnew Vue ({ el: ‘#app’, // 挂载到index.html中的#app上 render: h => h (App) // 用App.vue渲染})简单的一个首页,App.vue书写<template> <div> <h1>Success</h1> </div></template><style> h1 { background: #FAFBBB }</style>如上,我们需要引入vue,所以:npm install vue -S (自动安装2.x版本的vue)最后代码结构:index.html<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”> <meta http-equiv=“X-UA-Compatible” content=“ie=edge”> <link rel=“icon” href="#" type=“image/x-icon”> <title>doing</title></head><body> <div id=“app”></div></body></html>运行项目npm run dev具体的项目源码地址:https://github.com/Sotyoyo/do…源码与本文章稍有出入,后期会做到统一,希望给个star支持一下! ...

February 24, 2019 · 2 min · jiezi

学习webpack4 - 抽离公共代码

学习webpack4 - 基础配置学习webpack4 - HTML处理学习webpack4 - 样式处理学习webpack4 - ES6语法转化学习webpack4 - 第三方模块的使用学习webpack4 - 抽离公共代码…持续中=======================================================抽离公共代码我们在开发多个页面的项目的时候,有时候会在几个页面中引用某些公共的模块,这些公共模块多次被下载会造成资源浪费,如果把这些公共模块抽离出来只需下载一次之后便缓存起来了,这样就可以避免因重复下载而浪费资源,那么怎么在webpack中抽离出公共部分呢?方法如下:公共模块抽离举例:项目中分别有a.js, b.js, page1.js, page2.js这四个JS文件, page1.js 和 page2.js中同时都引用了a.js, b.js, 这时候想把a.js, b.js抽离出来合并成一个公共的js,然后在page1, page2中自动引入这个公共的js,怎么配置呢?如下: 配置webpack.config.js文件:module.exports = { //… //优化项配置 optimization: { // 分割代码块 splitChunks: { cacheGroups: { //公用模块抽离 common: { chunks: ‘initial’, minSize: 0, //大于0个字节 minChunks: 2 //抽离公共代码时,这个代码块最小被引用的次数 } } } }}完成!第三方模块抽离页面中有时会引入第三方模块,比如import $ from ‘jquery’; page1中需要引用,page2中也需要引用,这时候就可以用vendor把jquery抽离出来,方法如下:module.exports = { //… //优化项配置 optimization: { // 分割代码块 splitChunks: { cacheGroups: { //公用模块抽离 common: { chunks: ‘initial’, minSize: 0, //大于0个字节 minChunks: 2, //在分割之前,这个代码块最小应该被引用的次数 }, //第三方库抽离 vendor: { priority: 1, //权重 test: /node_modules/, chunks: ‘initial’, minSize: 0, //大于0个字节 minChunks: 2, //在分割之前,这个代码块最小应该被引用的次数 } } } }}注意:这里需要配置权重 priority,因为抽离的时候会执行第一个common配置,入口处看到jquery也被公用了就一起抽离了,不会再执行wendor的配置了,所以加了权重之后会先抽离第三方模块,然后再抽离公共common的,这样就实现了第三方和公用的都被抽离了。不加权重之前:jquery也被抽离到了公共js里加了权重之后: ...

February 20, 2019 · 1 min · jiezi

React入门:从零搭建一个React项目

一、初始化项目新建文件夹,文件名firstreact 文件夹名称不要用react,node这类关键字,后面使用插件时会发生错误。init项目环境,项目信息可默认或自行修改mkdir firstreactcd firstreactnpm init二、安装webpack新建gitignore文件,用于忽略安装的包文件,文件内容: node_modules安装webpack, 注意:我此处安装的webpack版本是4.28.4,webpack4和webpack2, webpack3的一些配置不同,具体参考webpack文档webpack中文文档npm i –save-dev webpack三、配置webpack环境新建文件夹,文件名:srcsrc目录下新建文件hello.js,文件内容:module.exports = function () { var element = document.createElement(‘h1’); element.innerHTML = ‘Hello React’; return element;};src目录下新建文件index.js,文件内容:var hello = require(’./hello.js’);document.body.appendChild(hello());新建文件webpack.config.js,一个最基础的webpack配置如下:const webpack = require(‘webpack’);const path = require(‘path’);var config = { entry: [ ‘./src/index.js’ ], // 打包入口文件 output: { path: path.resolve(__dirname, ‘dist’), filename: ‘bundle.js’ } // 打包输出文件};module.exports = config;执行webpack。执行完成后,根目录下会新增一个dist文件夹,文件夹下是打包出来的js文件bundle.jswebpack安装html-webpack-plugin,该插件将为你生成一个 HTML5 文件, 其中包括使用 script 标签的 body 中的所有 webpack 包。npm i –save-dev html-webpack-plugin html-webpack-plugin配置,webpack.config.js内容如下const webpack = require(‘webpack’);const path = require(‘path’);const HtmlwebpackPlugin = require(‘html-webpack-plugin’);var config = { entry: [ ‘./src/index.js’ ], // 打包入口文件 output: { path: path.resolve(__dirname, ‘dist’), filename: ‘bundle.js’ },// 打包输出文件 plugins: [ new HtmlwebpackPlugin({ title: ‘Hello React’, }) ]};module.exports = config;再次执行webpack,此时dist目录下生成了一个新文件index.htmlwebpack安装webpack-dev-server和webpack-cli,提供一个简单的 web 服务器,并且能够实时重新加载。npm install –save-dev webpack-dev-server webpack-cli修改webpack.configconst webpack = require(‘webpack’);const path = require(‘path’);const HtmlwebpackPlugin = require(‘html-webpack-plugin’);var config = { entry: [ ‘webpack/hot/dev-server’, ‘webpack-dev-server/client?http://localhost:3000’, ‘./src/index.js’ ], // 入口文件 output: { path: path.resolve(__dirname, ‘dist’), filename: ‘bundle.js’ }, // 打包输出文件 plugins: [ new HtmlwebpackPlugin({ title: ‘Hello React’ }), ]};module.exports = config;配置webpack启动的快方式,此处webpack4在启动服务是要求设置mode,告知 webpack 使用相应模式的内置优化。未设置会报一个警告。mode选项支持“development”“production”“none”,具体信息请阅文档 修改package.json文件:············ “scripts”: { “start”: “webpack-dev-server –mode=development –port 3000 –hot”, “build”: “webpack –mode=production” }···········启动服务,服务启动后打开浏览器访问http://localhost:3000/npm run dev三、优化开发环境css编译和js编译。现在开发时一般css都会使用扩展css语法,如less或sass,这时就需要在项目中安装css编译插件。此处以less为例。es6和es7语法也需要babel编译。const webpack = require(‘webpack’);const path = require(‘path’);const HtmlwebpackPlugin = require(‘html-webpack-plugin’);var config = { entry: [ ‘webpack/hot/dev-server’, ‘webpack-dev-server/client?http://localhost:3000’, ‘./src/index.js’ ], // 入口文件 output: { path: path.resolve(__dirname, ‘dist’), filename: ‘bundle.js’ }, // 打包输出文件 module: { rules: [ { test: /.less$/, use: [ { loader: ‘style-loader’ }, { loader: ‘css-loader’ }, { loader: ’less-loader’ } ] }, { test: /.js$/, exclude: /node_modules/, use: [ { loader: ‘babel-loader’ } ] } ] }, plugins: [ new HtmlwebpackPlugin({ title: ‘Hello React’ }), ]安装:npm i –save-dev less css-loader style-loader less-loadernpm i –save-dev babel-loader @babel/core @babel/preset-env @babel/preset-react 修改webpack.config.jsconst webpack = require(‘webpack’);const path = require(‘path’);const HtmlwebpackPlugin = require(‘html-webpack-plugin’);var config = { entry: [ ‘webpack/hot/dev-server’, ‘webpack-dev-server/client?http://localhost:3000’, ‘./src/index.js’ ], // 入口文件 output: { path: path.resolve(__dirname, ‘dist’), filename: ‘bundle.js’ }, // 打包输出文件 module: { rules: [ { test: /.less$/, use: [ { loader: ‘style-loader’ }, { loader: ‘css-loader’ }, { loader: ’less-loader’ } ] }, { test: /.js$/, exclude: /node_modules/, use: [ { loader: ‘babel-loader’ } ] } ] }, plugins: [ new HtmlwebpackPlugin({ title: ‘Hello React’ }), ]};module.exports = config;根目录下新建.babelrc文件,配置文件内容如下{ “presets”: [ “@babel/preset-env”, “@babel/preset-react” ]}在src目录下新建文件index.less,文件内容如下body{ h1{ color: green; }}修改src目录下的index.js文件:import hello from ‘./hello.js’;import ‘./index.less’;document.body.appendChild(hello());再次启动服务npm run start目前为止完成了一个最基础的项目结构,后面需要使用其他框架的话再此基础上修改。在这过程中因各个插件工具的版本不同可能会发生不同错误,遇到错误时,请查询相关文档。四、在项目中使用React安装react。npm i –save-dev react react-dom修改src目录下index.js,文件内容如下:import React from ‘react’;import ReactDOM from ‘react-dom’;import ‘./index.less’;class APP extends React.Component { render() { return (<h1>Hello React</h1>) }}ReactDOM.render(<APP/>, document.getElementById(‘content’));在src目录下新建index.html,在html增加挂载节点content。 文件内容如下:<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title><%= htmlWebpackPlugin.options.title %></title></head><body> <div id=“content”></div></body></html>对应修改webpack.config.js文件,为htmlWebpackPlugin修改template············ plugins: [ new HtmlwebpackPlugin({ title: ‘Hello React’, template: ‘./src/index.html’ }), ] ············目录结构为:│ .babelrc│ .gitignore│ package.json│ webpack.config.js│ └─src hello.js index.html index.js index.less ...

February 20, 2019 · 2 min · jiezi

学习webpack4.x - 第三方库的使用

学习webpack4.x - 基础配置学习webpack4.x - HTML处理学习webpack4.x - 样式处理学习webpack4.x - ES6语法转化学习webpack4.x - 第三方模块的使用 // 学习webpack4.x - 图片处理(未整理)…持续中=======================================================第三方库的使用注意:开始之前以下内容之前,需要配置一些webpack的基础配置,传送门:学习webpack4.x - 基础配置当前目录:在项目中,经常会引用一些第三方模块, 比如jquery, lodash等,但是这些第三方模块怎么在webpack中配置呢?以jquery为例子:首先先安装jquery:yarn add jquery方法一:直接引用打开src/index.js文件,输入:import $ from ‘jquery’;console.log($);尝试运行:npm run dev, 结果如下:结果中会发现,已经引入完成了,在当前这个文件中可以随便使用jquery了。但是这种方法是不能将jquery暴露到window全局的, 执行console.log(window.$),结果是undefined,那么怎么暴露到window呢?如下:方法二:暴露到window将第三方库暴露在全局可以用expose-loader, 下面通过这个将jquery变成 $ 暴露到window:step1:配置webpack.config.js文件:// 模块配置module.exports = { module: { //… rules: [ //… { test: require.resolve(‘jquery’), use: ’expose-loader?$’ //把jquery变成 $ 暴露到window } ] }}step2: 安装expose-loaderyarn add expose-loader -D尝试运行:npm run dev, 这时候发现console.log(window.$)有值了!以上两种做法很麻烦,要import $ from ‘jquery’这样引入jquery, 那么怎么样能在不同模块直接使用 $ 而不引入jquery呢 ? 如下:方法三:模块中注入配置webpack.config.js文件:let Webpack = require(‘webpack’);// 模块配置module.exports = { //… //插件配置 plugins: [ //… // 注入对象 new Webpack.ProvidePlugin({ ‘$’: ‘jquery’ //把jquery变成 $ 注入到模块 }) ]}只需要在webpack.config.js中这样配置下插件, 就可以直接在需要用到jquery的模块中使用$了, 而不用再通过import引入jquery,但是这种方法不能暴露到window中哦。方法四:html中直接引入这种方法是通过html的script标签,直接把第三方库引入进来,不需要yarn add去安装它,方法如下:step1: 打开src/index.html文件:<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”> <meta http-equiv=“X-UA-Compatible” content=“ie=edge”> <title>丸子</title> <script src=“https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script></head><body> <div>我通过script引入了一个jquery</div></body></html>step2: 打开src/index.js,引入jqueryimport $ from ‘jquery’;console.log($)step3: 配置webpack.config.js文件module.exports = { //… //外部引入的,不要打包 externals: { jquery: “$” } //…}这种方法中webpack.config.js中的externals的配置是告诉webpack,这个模块是第三方模块,不要打包进去,这样可以避免因打包进去而文件过大,造成资源浪费。同样,这种方法也是可以暴露给window的。 ...

February 19, 2019 · 1 min · jiezi

学习webpack4.x - ES6语法转化

学习webpack4.x - 基础配置学习webpack4.x - HTML处理学习webpack4.x - 样式处理学习webpack4.x - ES6语法转化 // 学习webpack4.x - 全局变量引入(未整理) // 学习webpack4.x - 图片处理(未整理)…持续中=======================================================ES6语法转化注意:开始之前以下内容之前,需要配置一些webpack的基础配置,传送门:学习webpack4.x - 基础配置当前目录结构为:index.js 文件内容:require(’./index.css’);require(’./index.scss’);webpack.config.js文件内容:let path = require(‘path’);let HtmlWebpackPlugin = require(‘html-webpack-plugin’);let MiniCssExtractPlugin = require(‘mini-css-extract-plugin’); //抽离CSSlet OptimizeCssPlugin = require(‘optimize-css-assets-webpack-plugin’); //优化项,比如压缩css等let UglifyJsPlugin = require(‘uglifyjs-webpack-plugin’); //压缩jsmodule.exports = { // mode: ‘development’, //优化项配置 optimization: { minimizer: [ new OptimizeCssPlugin(), new UglifyJsPlugin({ cache: true, //缓存 parallel: true, //并发打包 sourceMap: true //源码映射便于调试 }) ] }, //开一个本地服务 devServer: { port: 3000, //端口号 progress: true, //进度条 contentBase: ‘./dist’, //指定目录运行服务 open: true //自动打开浏览器 }, entry: ‘./src/index.js’, output: { filename: ‘bundle.js’, path: path.resolve(__dirname, ‘dist’) }, // 模块配置 module: { rules: [{ test: /.(css|scss)$/, use: [MiniCssExtractPlugin.loader, ‘css-loader’, ‘postcss-loader’, ‘sass-loader’] }] }, //插件配置 plugins: [ new HtmlWebpackPlugin({ template: ‘./src/index.html’, //原始文件 filename: ‘index.html’, //打包后的文件名称 hash: true, //hash }), new MiniCssExtractPlugin({ filename: ‘main.css’ //抽离出的css文件名称 }) ]}package.json文件内容:{ “name”: “webpack”, “version”: “1.0.0”, “main”: “index.js”, “license”: “MIT”, “scripts”: { “dev”: “webpack –mode development && webpack-dev-server”, “build”: “webpack –mode production” }, “devDependencies”: { “autoprefixer”: “^9.4.7”, “css-loader”: “^2.1.0”, “html-webpack-plugin”: “^3.2.0”, “less”: “^3.9.0”, “less-loader”: “^4.1.0”, “mini-css-extract-plugin”: “^0.5.0”, “node-sass”: “^4.11.0”, “optimize-css-assets-webpack-plugin”: “^5.0.1”, “postcss-loader”: “^3.0.0”, “sass-loader”: “^7.1.0”, “style-loader”: “^0.23.1”, “uglifyjs-webpack-plugin”: “^2.1.1”, “webpack”: “^4.29.4”, “webpack-cli”: “^3.2.3”, “webpack-dev-server”: “^3.1.14”, “webpack-html-plugin”: “^0.1.1” }}将ES6转化为ES5step1: 打开src/index.js,输入:const fn = () => { console.log(‘丸子’);}fn ();step2: 配置webpack.config.js文件:将ES6转成ES5,需要babel-loader,配置规则为:module.exports = { //… module: { //… { test: /.js$/, use: { loader: ‘babel-loader’, options: { presets: [’@babel/preset-env’] // 根据目标浏览器自动转换为相应es5代码 } } } }};step3: 安装插件:yarn add babel-loader @babel/core @babel/preset-env -D尝试运行: npm run dev, 成功!如下图: ...

February 19, 2019 · 2 min · jiezi

学习webpack4.x - 样式处理

学习webpack4.x - 基础配置学习webpack4.x - HTML处理学习webpack4.x - 样式处理 // 学习webpack4.x - ES6语法转化(未整理) // 学习webpack4.x - 全局变量引入(未整理) // 学习webpack4.x - 图片处理(未整理)…持续中=======================================================样式处理注意:开始之前以下内容之前,需要配置一些webpack的基础配置,传送门:学习webpack4.x - 基础配置当前目录为:index.html文件内容:<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”> <meta http-equiv=“X-UA-Compatible” content=“ie=edge”> <title>丸子</title></head><body> <div>我是一个html原文件,我想打包后自动把打包后的js插入到我的文件中, 并且把我自动放到打包后的那个目录中…</div></body></html>package.json文件内容:{ “name”: “webpack”, “version”: “1.0.0”, “main”: “index.js”, “license”: “MIT”, “scripts”: { “dev”: “webpack –mode development && webpack-dev-server”, “build”: “webpack –mode production” }, “devDependencies”: { “html-webpack-plugin”: “^3.2.0”, “webpack”: “^4.29.4”, “webpack-cli”: “^3.2.3”, “webpack-dev-server”: “^3.1.14”, “webpack-html-plugin”: “^0.1.1” }}webpack.config.js文件内容:let path = require(‘path’);let HtmlWebpackPlugin = require(‘html-webpack-plugin’);module.exports = { // mode: ‘development’, //开一个本地服务 devServer: { port: 3000, //端口号 progress: true, //进度条 contentBase: ‘./dist’, //指定目录运行服务 open: true //自动打开浏览器 }, entry: ‘./src/index.js’, output: { filename: ‘bundle.js’, path: path.resolve(__dirname, ‘dist’) }, //插件配置 plugins: [ new HtmlWebpackPlugin({ template: ‘./src/index.html’, //原始文件 filename: ‘index.html’, //打包后的文件名称 hash: true, //hash }) ]}下面开始配置css:css配置首先看看这种直接引入css的方法行不行:step1: 打开src目录,新建index.css文件,并输入:body { background: red;}step2: 打开src目录的index.html,直接把index.css文件引入,如下: <link rel=“styleSheet” href="./index.css" />尝试运行: npm run dev,打包成功, 结果如下:查看打包好的dist目录发现,这种直接引用css的方法虽然没有报错,但是在dist/index.html中只是原封不动的把<link rel=“styleSheet” href="./index.css" />输出了,css并没有生效,所以这种直接引入的方法不可行,那么怎么样把css打包进来呢?方法如下:把css作为模块引入文件:step1:打开src/index.js文件,引入要打包的css文件:index.cssrequire(’./index.css’);尝试运行:npm run dev, 结果如下:报了个错说需要一个合适的loader来处理这个css文件,接下来进行相应的配置:step2: 打开webpack.config.js文件,进行module配置: // 模块配置 module: { rules: [ {test: /.css$/, use: [‘style-loader’, ‘css-loader’]} ] },安装style-loader, css-loader:yarn add style-loader css-loader -D尝试运行:npm run dev,body背景颜色变红,打包成功!结果如下:补充loader:webpack打包时,对于css,图片,或者其他的语法集比如jsx等这些是没有办法直接加载的,需要对应的loader把资源进行转化并加载,执行顺序:从右到左,从下到上。css-loader: 解析css中的代码,像是@import这种语法,比如在index.css中引入另一个other.css文件,需要@import ‘./other.css’。style-loader: 将css模块作为样式插入到DOM中。less配置step1:打开src目录,新建index.less文件,并输入:body { div { width: 500px; height: 500px; background: yellow; }}step2: 打开src/index.js文件,引入index.less文件:require(’./index.less’);step3: 打开webpack.config.js文件,修改module部分:// 模块配置 module: { rules: [ {test: /.(css | less)$/, use: [‘style-loader’, ‘css-loader’, ’less-loader’]}, ] },step4: 安装less, less-loader:yarn add less less-loader -D尝试运行,成功!结果如下: ...

February 19, 2019 · 1 min · jiezi

学习webpack4.x - HTML处理

学习webpack4.x - 基础配置- 学习webpack4.x - HTML处理 // 学习webpack4.x - 样式处理(未整理) // 学习webpack4.x - ES6语法转化(未整理) // 学习webpack4.x - 全局变量引入(未整理) // 学习webpack4.x - 图片处理(未整理)…持续中=======================================================HTML处理有些时候,我们项目里的html一开始没有创建,但是打包的时候呢希望自动生成html入口页面并且这个html文件可以自动引入打包后的JS文件等,而且这个html文件自动被放到打包后的目录中,这种情况下怎么通过webpack配置呢?注意:开始之前以下内容之前,需要配置一些webpack的基础配置,传送门:学习webpack4.x - 基础配置安装插件安装自动生成html文件需要的插件: webpack-html-plugin,它可以简化html文件的创建。yarn add webpack-html-plugin -D基本配置当前目录如下:下面开始配置webpack.config.js文件:打开webpack.config.js文件, 引入 webpack-html-plugin 插件,并且在plugin中配置该插件:let HtmlWebpackPlugin = require(‘html-webpack-plugin’);//插件配置 plugins: [ new HtmlWebpackPlugin({ template: ‘./src/index.html’, //原始文件 filename: ‘index.html’, //打包后的文件名称 }) ]尝试运行:npm run dev成功!运行后自动生成了build目录,并且在build目录中自动生成了index.html文件,结果如下: 打包后的目录: 浏览器中:webpack-html-plugin 常用配置选项title: 用于生成的HTML文档的标题filename: 要将HTML写入的文件, 默认index.htmltemplate: webpack需要模板的路径inject: 默认值truetrue 或 body: 将脚本放在body底部。head: 将脚本放在head元素中。favicon: 将给定的favicon路径添加到输出HTMLmeta: 允许注入meta-tagsminify: 缩小输出hash: 如果webpack为所有包含的脚本和CSS文件附加唯一的编译哈希,对缓存清除很有用。minify配置://插件配置 plugins: [ new HtmlWebpackPlugin({ template: ‘./src/index.html’, //原始文件 filename: ‘index.html’, //打包后的文件名称 minify: { collapseWhitespace: true //折叠空行 } }) ]效果:hash配置://插件配置 plugins: [ new HtmlWebpackPlugin({ template: ‘./src/index.html’, //原始文件 filename: ‘index.html’, //打包后的文件名称 hash: true, //hash }) ]效果:

February 18, 2019 · 1 min · jiezi

【持续中】学习webpack4.x - 基础配置

学习webpack4.x - 基础配置 //- 学习webpack4.x - HTML处理(未整理) // 学习webpack4.x - 样式处理(未整理) // 学习webpack4.x - ES6语法转化(未整理) // 学习webpack4.x - 全局变量引入(未整理) // 学习webpack4.x - 图片处理(未整理)=======================================================基础配置安装yarn init -y 初始化项目yarn add webpack webpack-cli -D尝试运行step1: 新建src目录, 在src目录下新增一个index.js文件,并输入:console.log(‘丸子’);step2: 打开package.json文件,添加一个脚本:“scripts”: { “build”: “webpack” }尝试运行一次:npm run build打包成功了!当前目录结构为:当前目录中并没有webpack.config.js这个文件却还能打包成功?因为在webpack4中不必一定要有配置文件了,也不必一定要指定一个入口和出口才能打包成功。它会默认找到./src/index.js 作为默认入口点并且在 ./dist/main.js 中输出模块包,但是这样不是很灵活,因为名字之类的都指定了,那么怎么自己配置这些东西呢?入口&出口配置step1: 根目录下新建一个webpack.config.js文件step2: 打开webpack.config.js文件,进行入口和出口配置:let path = require(‘path’);module.exports = { entry: ‘./src/index.js’, output: { filename: ‘bundle.js’, path: path.resovle(__dirname, ‘dist’) }}尝试运行一下:npm run build运行成功了。当前目录结构为:两个配置:entry: 入口配置 用来指定入口起点,默认./src,进入入口起点后,webpack会找出有哪些模块和库是入口起点直接和间接依赖的。output: 出口配置 用来指定在哪里输出所创建的bundles,默认./dist,其中:filename: 输出文件名称path: 输出文件存放路径,该路径是绝对路径上面结果中还看到有一个warning说mode没有设置,这个mode是模式,接下来配置下这个mode模式配置mode(模式):分别是production(生产) 和 development(开发) 模式方法一:打开webpack.config.js文件,配置mode方法二:打开package.json文件,配置脚本尝试运行,npm run dev //开发环境npm run build //生产环境警告消失,结果如下:(补充)启动本地服务在本地开发的时候, 总是要自己在浏览器中打开文件,这样很不好, 那么怎么在本地开一个服务呢? step1: 安装webpack-dev-serveryarn add webpack-dev-server -Dstep2: 打开webpack.config.js文件,配置webServerdevServer: { port: 3000, //端口号 progress: true, //进度条 contentBase: ‘./dist’, //指定目录运行服务 open: true //自动打开浏览器}step3: 打开package.json文件,更改下脚本"scripts": { “dev”: “webpack –mode development && webpack-dev-server”, “build”: “webpack –mode production” }尝试运行:npm run dev成功!结果如下:此时浏览器会自动打开 http://localhost:3000/

February 18, 2019 · 1 min · jiezi

webpack4.0配置记录(2)

接上一篇webpack4.0配置记录(1),继续记录学习webpack配置。定义环境变量new Webpack.DefinePlugin({//用来定义全局环境变量 DEV:JSON.stringify(‘dev’), FLAG:’true’}),webpack简单优化noParsemodule:{ noParse:’/jquery/’,//不去解析设置的包所依赖的关系,如jquery}ignorePluginmodule:{ noParse:’/jquery/’,//不去解析设置的包所依赖的关系 rules:[ { test:/.js$/, exclude:/node_modules/, include:path.resolve(‘src’), use:{ loader:‘babel-loader’, options:{ presets:[ ‘@babel/preset-env’, ‘@babel/preset-react’ ] } } } ]}通过exclude排除和include包含某些模块 另外也可以使用webpack自带的ignorePlugin插件排除某些包,减少体积。new webpack.IgnorePlugin(/./locale/,/moment/),以上配置忽略了时间格式化moment.js中的语言包happypack多线程打包let Happypack=require(‘happypack’)…module.exports={ module:{ noParse:’/jquery/’,//不去解析设置的包所依赖的关系 rules:[ { test:/.js$/, exclude:/node_modules/, include:path.resolve(‘src’), use:‘Happypack/loader?id=js’ // use:{ // loader:‘babel-loader’, // options:{ // presets:[ // ‘@babel/preset-env’, // ‘@babel/preset-react’ // ] // } // } } ] }, plugins:[ new Happypack({ id:‘js’, use:[ { loader:‘babel-loader’, options:{ presets:[ ‘@babel/preset-env’, ‘@babel/preset-react’ ] } } ] }) ]}webpack内置功能(1)tree-shaking (2)scope-hosting 这两项优化只在生产环境下有效抽离公共代码module.exports={ optimization:{ splitChunks:{//分割代码块 cacheGroups:{//缓冲组 common:{ chunks:‘initial’, minSize:0,//抽离模块最小粒度是0 minChunks:2//表示代码块用过2次以上就要抽离 }, vendor:{ priority:1,//相当于权重,先抽离第三方模块,如果不设置该属性,分割代码块将从上到下,无法抽离第三方模块。 test:/node_modules/, chunks:‘initial’, minSize:0,//抽离模块最小是0 minChunks:2//表示用过2次以上就要抽离 } } } },}文件热更新devServer:{ hot:true},plugins:[ new webpack.NamedModulesPlugin(),//打印更新的模块路径 new webpack.HotModuleReplacementPlugin()//热更新]7.可以使用dllPlugin动态链接库优化 DllPlugin 和 DllReferencePlugin提供了以大幅度提高构建时间性能的方式拆分软件包的方法。原理是将特定的第三方NPM包模块提前构建,然后通过页面引入。这不仅能够使得vendor文件可以大幅度减小,同时,也极大的提高了构件速度。网上别的大神有一篇文章写的很详细,可以参考,传送门。 以上就是一些自己在学习webpack4.0配置过程中的一些学习记录,写出来和大家分享,如果有错误,还望告知。个人博客同步更新,欢迎关注交流!不要忘了点个赞,谢谢! ...

February 2, 2019 · 1 min · jiezi

webpack4.0配置记录(1)

趁着假期闲暇,练习下webpack4.0的一些配置。webpack4优化压缩js和css方式let UglifyJsPlugin = require(“uglifyjs-webpack-plugin”);let OptimizeCSSAssetsPlugin = require(“optimize-css-assets-webpack-plugin”);optimization: {//优化项 minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true,//并发打包 sourceMap: true // set to true if you want JS source maps }),//开发环境下不压缩js,想启用压缩功能,需要把mode切换为production new OptimizeCSSAssetsPlugin({}) ]},注意:若想优化生效,必须将mode改为production模式 详情见npm官网 expose-loader 暴露全局loader,称为内联loader。到目前为止,有内联loader,普通normal loader,前置loader (pre loader),后置loader (post loader)在项目中引入jquery类似模块方式webpack.config.js配置let webpack =require(‘webpack’);plugins:[//存放webpack插件 new webpack.ProvidePlugin({//在每个模块中注入$ ‘$’:‘jquery’ })],webpack引入基层模块方式expose-loader暴露到全局window上providePlugin给每个模块提供$cdn方式引入不打包,webpack需要配置externals打包文件分类new MiniCssExtractPlugin({ filename:‘css/main.css’}),将css打包在css文件夹中{ test:/(.png|.jpg)$/, use:{ loader:‘url-loader’, options:{ limit:50*1024, outputPath:‘images/’, //publicPath:’’ } }}图片打包路径前配置publicPath即可。生成source-map便于调试,几种不同选项(1)增加源码映射文件,便于调试。标示报错文件行和列,大而全文件devtool:‘source-map’(2)不会产生单独文件,但是可以显示行和列devtool:’eval-source-map’(3)不会产生列,但是是一个单独的映射文件,用于调试devtool:‘cheap-module-source-map’(4)不会产生文件,集成在打包后的文件中,也不会产生列devtool:‘cheap-module-eval-source-map’监听文件变动,实时打包watch:true,watchOptions:{//监听选项 poll:1000,//每秒问我1000次,是否打包 aggregateTimeout:500,//防抖 ignored:/node_modules///不需要监控的文件},webpack插件应用cleanWebpackPlugin(需要安装依赖模块)new CleanWebpackPlugin(’./dist’)//先清空dist目录下的文件在打包copyWebpackPlugin(需要安装依赖模块)new CopyWebpackPlugin([ { from:’./doc’, to:’./dist’ }//可以写多个,拷贝多个目录文件])bannerPlugin(内置插件)//添加版权注释信息new Webpack.BannerPlugin(‘make by mgl 2019-2-1’)运行打包命令后,可在打包文件中看到注释信息npm run devwebpack中devServer几种配置(1)单纯配置跨域代理方式proxy:{ ‘/api’:{ target:‘http://localhost:3000’, pathRewrite:{’/api’:’’} }}(2)前端单纯mock数据before(app){ app.get(’/user’,(req,res)=>{ res.json({name:‘mgl-before’}); })}(3)有服务端,不用代理来处理,在服务端中启动webpack,用服务端端口//expresslet express = require(’express’);let webpack=require(‘webpack’);//引入中间件let middle=require(‘webpack-dev-middleware’);let config=require(’./webpack.config.js’);let compiler=webpack(config);//webpack处理返回结果let app=express();app.use(middle(compiler));app.get(’/user’,(req,res)=>{ res.json({name:‘mgl’});})app.listen(3000)resolve属性配置resolve:{//解析第三方模块 modules:[path.resolve(’node_modules’)], extensions:[’.js’,’.css’,’.vue’,’.json’]//指定解析后缀名称,从左向右 // mainFields:[‘style’,‘main’]//指定引入模块的先后顺序 // mainFiles:[],//指定入口文件的名字,默认是index.js // alias:{//配置别名 // bootstrap:‘bootstrap/dist/css/bootstrap.css’ // }},陆续更新中,欢迎关注个人博客,同步更新中! ...

February 1, 2019 · 1 min · jiezi

vue2+webpack4+scss

作者:心叶时间:2019年01月30日 10:37前要由于webpack的配置具有实时性,之前搭建的vue2项目是基于webpack2,因此,特地在此维护一篇关于webpack4搭建vue2项目的说明,会及时更新(github地址:https://github.com/yelloxing/…)。基础配置npm install –save-dev vue vue-router由于是搭建vue2项目,首先需要安装用到的vue和vue路由。npm install –save-dev webpack webpack-cli接着,安装webpack,建立好空的webpack.config.js,如下:module.exports = { entry: [’./src/entry.js’], output: { path: __dirname, filename: ‘build/main.js’ }, resolve: { alias: { ‘vue’: ‘vue/dist/vue.js’ } }, module: { rules: [] }};上面的配置和webpack2的没有区别,这里不再赘述了(entry:打包入口,output:打包文件存放地址,resolve的配置是因为vue2在开发和生成模式对象不统一)。解析vue2npm install –save-dev vue-template-compiler vue-loader首先安装vue模板解析器,接着,在webpack.config.js中配置一下:…rules: [{ test: /.vue$/, use: [‘vue-loader’]}]…配置的时候,其实就是加上上面这二句代码。到此为止,最简单的项目就搭建好了。为了方便执行,在package.json中添加下面脚本:“scripts”: { “release”: “node_modules/.bin/webpack”}然后在命令行执行:npm run release搭建开发环境上面的打包方式时候开发完毕以后,发布代码,如果是开发中,我们希望实时刷新。首先安装服务器:npm install –save-dev webpack-dev-server接着在webpack中配置:…devServer: { contentBase: ‘./’, compress: true, host: ’localhost’, port: ‘20000’, hot: true, inline: true, historyApiFallback: true},plugins: [ new webpack.HotModuleReplacementPlugin()],module:…然后,在package.json中添加下面脚本:“scripts”: { “dev”:“node_modules/.bin/webpack-dev-server –open”, “release”: “node_modules/.bin/webpack”}执行下面命令:npm run dev浏览器会自动打开:localhost:20000完善vue配置npm install –save-dev vue-style-loader css-loader上面是添加项目中对css的解析器,再在webpack.config.js中添加如下配置,即可开发css代码:{ test: /.css$/, use: [‘vue-style-loader’, ‘css-loader’]}添加对scss的支持npm install –save-dev sass-loader node-sass然后,添加下面配置: { test: /.scss$/, use: [“vue-style-loader”, “css-loader”, “sass-loader”]}添加对图片的支持npm install –save-dev file-loader然后,添加下面配置:{ test: /.(png|jpg|jpeg|gif|bmp)$/, use: [‘file-loader?limit=7000&name=build/image/[name].[ext]’]}如果你使用的图片格式不被上面的test包含,需要手动额外添加即可。后记完整的代码维护在github中,会及时更新:https://github.com/yelloxing/…希望对你学习vue2有所帮助 :) ...

January 30, 2019 · 1 min · jiezi

webpack4 配置文件分离

webpack4 配置文件分离webpack根据开发和生成环境一般可以将配置文件拆分,拆分dev和prod两种环境|- package.json |- /build |- webpack.base.js |- webpack.dev.js |- webpack.prod.js在scripts里修改相应的命令"dev": “webpack-dev-server –config build/webpack.dev.js”,“build”: “webpack –config build/webpack.prod.js"使用webpack-merge,用以合并通用配置文件与开发环境配置文件// webpack.dev.jsconst path = require(‘path’);const webpack = require(‘webpack’);const merge = require(‘webpack-merge’);const baseWebpackConfig = require(’./webpack.base’);module.exports = merge(baseWebpackConfig, { mode: ‘development’, devServer: { host: ‘127.0.0.1’, port: 80, contentBase: path.join(__dirname, ‘dist’), open: false, hot: false, disableHostCheck: true, proxy: {}, before () {} }, plugins: [ // 启用 HMR new webpack.HotModuleReplacementPlugin({}) ]});// webpack.prod.jsconst baseWebpackConfig = require(’./webpack.base’);module.exports = merge(baseWebpackConfig, { mode: ‘production’}); ...

January 24, 2019 · 1 min · jiezi

webpack4使用笔记之devServer

devServerdevServer需要webpack-dev-server配合使用,借助webpack-dev-server快速启动一个静态服务➔ host::指定 ip 或域名➔ port: 指定端口➔ contentBase:指定根目录➔ open:是否自动打开浏览器➔ hot:是否启用热替换➔ disableHostCheck:禁用 host 检查➔ proxy:代理请求➔ before:自定义中间件"scripts": { “dev”: “webpack-dev-server –mode development”}当 mode为 development 并且启用 HotModuleReplacementPlugin 插件时,会具备 hot reload` 的功能。即当源码文件变化时,会即时更新当前页面,以便看到最新的效果。HotModuleReplacementPlugin启用热替换模块(Hot Module Replacement),也被称为 HMR…devServer: { host: ‘127.0.0.1’, // 指定 ip 或域名 port: 80, contentBase: path.join(__dirname, ‘dist’), // 指定根目录 open: true, // 否自动打开浏览器 hot: true, // 是否启用热替换 disableHostCheck: true, // 禁用 host 检查 proxy: {}, before () {} // 自定义中间件},plugins: { new webpack.HotModuleReplacementPlugin({ // Options… })} ...

January 24, 2019 · 1 min · jiezi

webpack4初学习

webpack基于node,因此想要学习webpack首先要安装node。webpack4要安装node8.2以上版本。1、安装webpack全局安装,方便以后使用npm install webpack webpack-cli -ga. 或者本地安装mkdir webpack-testcd webpack-testnpm init //初始化npm,文件夹多出package.jsonnpm i webpack webpack-cli -D★ npm i -D 是 npm install –save-dev 的简写,是指安装模块并保存到 package.json 的 devDependencies中,主要在开发环境中的依赖包b. 根目录下创建main.js文件document.write(‘Hello world!’)c. 初试webpackwebpack main.js -o bundle.js // 打包main.js文件,包文件bundle.js未完,待续

January 21, 2019 · 1 min · jiezi

vue + koa2 + webpack4 构建ssr项目

什么是服务器端渲染 (SSR)?为什么使用服务器端渲染 (SSR)?看这 Vue SSR 指南技术栈vue、vue-router、vuexkoa2webpack4axiosbabel、eslintcss、stylus、postcsspm2目录层次webpack4-ssr-config├── client # 项目代码目录│ ├── assets # css、images等静态资源目录│ ├── components # 项目自定义组件目录│ ├── plugins # 第三方插件(只能在客户端运行)目录,比如 编辑器│ ├── store # vuex数据存储目录│ ├── utils # 通用Mixins目录│ ├── views # 业务视图.vue和route路由目录│ ├── app.vue # │ ├── config.js # vue组件、mixins注册,http拦截器配置等等│ ├── entry-client.js # 仅运行于浏览器│ ├── entry-server.js # 仅运行于服务器│ ├── index.js # 通用 entry│ ├── router.js # 路由配置和相关钩子配置│ └── routes.js # 汇聚业务模块所有路由route配置├── config # 配置文件目录│ ├── http # axios封装的http请求│ ├── logger # .vue里this.[log,warn,info,error]和koa2里 logger日志输出│ ├── middle # koa2中间件目录│ │ ├── errorMiddleWare.js # 错误处理中间件│ │ ├── proxyMiddleWare.js # 接口代理中间件│ │ └── staticMiddleWare.js # 静态资源中间件│ ├── eslintrc.conf.js # eslint详细配置│ ├── index.js # server入口│ ├── koa.server.js # koa2服务详细配置│ ├── setup.dev.server.js # koa2开发模式实现hot热更新│ ├── vue.koa.ssr.js # vue ssr的koa2中间件。匹配路由、请求接口生成dom,实现SSR│ ├── webpack.base.config.js # 基本配置 (base config) │ ├── webpack.client.config.js # 客户端配置 (client config)│ └── webpack.server.config.js # 服务器配置 (server config)├── dist # 代码打包目录├── log # pm2日志输出目录├── node_modules # node包├── .babelrc # babel配置├── .eslintrc.js # eslint配置├── .gitignore # git配置├── app.config.js # 端口、代理配置、webpack配置等等├── constants.js # 存放常量├── favicon.ico # ico图标├── index.template.ejs # index模板├── package.json # ├── package-lock.json # ├── pm2.config.js # 项目pm2配置├── pm2.md # pm2的api文档├── postcss.config.js # postcss配置文件└── README.md # 文档源码结构构建使用 webpack 来打包我们的 Vue 应用程序,参考官方分成3个配置,这里使用的webpack4和官方的略有区别。├── webpack.base.config.js # 基本配置 (base config) ├── webpack.client.config.js # 客户端配置 (client config)├── webpack.server.config.js # 服务器配置 (server config)具体webpack配置代码这里省略…对于客户端应用程序和服务器应用程序,我们都要使用 webpack 打包 - 服务器需要「服务器 bundle」然后用于服务器端渲染(SSR),而「客户端 bundle」会发送给浏览器,用于混合静态标记。基本流程如下图:项目代码├── entry-client.js # 仅运行于浏览器├── entry-server.js # 仅运行于服务器├── index.js # 通用 entry├── router.js # 路由配置├── routes.js # 汇聚业务模块所有路由route配置index.jsindex.js 是我们应用程序的「通用 entry」,对外导出一个 createApp 函数。这里使用工厂模式为为每个请求创建一个新的根 Vue 实例,从而避免server端单例模式,如果我们在多个请求之间使用一个共享的实例,很容易导致交叉请求状态污染。entry-client.js:客户端 entry 只需创建应用程序,并且将其挂载到 DOM 中:import Vue from ‘vue’import { createApp } from ‘./index’// 引入http请求import http from ‘./../config/http/http’……const { app, router, store } = createApp()if (window.INITIAL_STATE) { store.replaceState(window.INITIAL_STATE) // 客户端和服务端保持一致 store.state.$http = http}router.onReady(() => { …… Promise.all(asyncDataHooks.map(hook => hook({ store, router, route: to }))) .then(() => { bar.finish() next() }) .catch(next) }) // 挂载 app.$mount(’#app’)})entry-server.js:服务器 entry 使用 default export 导出函数,并在每次渲染中重复调用此函数。此时,除了创建和返回应用程序实例之外,还在此执行服务器端路由匹配和数据预取逻辑。import { createApp } from ‘./index’// 引入http请求import http from ‘./../config/http/http’// 处理ssr期间cookies穿透import { setCookies } from ‘./../config/http/http’// 客户端特定引导逻辑……const { app } = createApp()// 这里假定 App.vue 模板中根元素具有 id="app"app.$mount(’#app’)export default context => { return new Promise((resolve, reject) => { const { app, router, store } = createApp() const { url } = context …… // 设置服务器端 router 的位置,路由配置里如果设置过base,url需要把url.replace(base,’’)掉,不然会404 router.push(url) // 等到 router 将可能的异步组件和钩子函数解析完 router.onReady(() => { …… // SSR期间同步cookies setCookies(context.cookies || {}) // http注入到rootState上,方便store里调用 store.state.$http = http // 使用Promise.all执行匹配到的Component的asyncData方法,即预取数据 Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({ store, router, route: router.currentRoute, }))).then(() => { // 在所有预取钩子(preFetch hook) resolve 后, // 我们的 store 现在已经填充入渲染应用程序所需的状态。 // 当我们将状态附加到上下文, // 并且 template 选项用于 renderer 时, // 状态将自动序列化为 window.__INITIAL_STATE__,并注入 HTML。 context.state = store.state resolve(app) }).catch(reject) }, reject) })}router.js、routes.js、store.jsrouter和store也都是工厂模式,routes是业务模块路由配置的集合。routerimport Vue from ‘vue’import Router from ‘vue-router’import routes from ‘./routes’Vue.use(Router)export function createRouter() { const router = new Router({ mode: ‘history’, fallback: false, // base: ‘/ssr’, routes }) router.beforeEach((to, from, next) => { /todo * 做权限验证的时候,服务端和客户端状态同步的时候会执行一次 * 建议vuex里用一个状态值控制,默认false,同步时直接next,因为服务端已经执行过。 * / next() }) router.afterEach((route) => { /todo/ }) return router}routeimport testRoutes from ‘./views/test/routes’import entry from ‘./app.vue’const home = () => import(’./views/home.vue’)const routes = [ { path: ‘/’, component: home }, { path: ‘/test’, component: entry, children: testRoutes },]export default routesstoreimport Vue from ‘vue’import Vuex from ‘vuex’import test from ‘./modules/test’Vue.use(Vuex)export function createStore() { return new Vuex.Store({ modules: { test } })}Http请求http使用Axios库封装/ * Created by zdliuccit on 2019/1/14. * @file axios封装 * export default http 接口请求 * export addRequestInterceptor 请求前拦截器 * export addResponseInterceptor 请求后拦截器 * export setCookies 同步cookie */import axios from ‘axios’const currentIP = require(‘ip’).address()const appConfig = require(’./../../app.config’)const defaultHeaders = { Accept: ‘application/json, text/plain, /; charset=utf-8’, ‘Content-Type’: ‘application/json; charset=utf-8’, Pragma: ’no-cache’, ‘Cache-Control’: ’no-cache’,}Object.assign(axios.defaults.headers.common, defaultHeaders)if (!process.browser) { axios.defaults.baseURL = http://${currentIP}:${appConfig.appPort}}const methods = [‘get’, ‘post’, ‘put’, ‘delete’, ‘patch’, ‘options’, ‘request’, ‘head’]const http = {}methods.forEach(method => { http[method] = axios[method].bind(axios)})export const addRequestInterceptor = (resolve, reject) => { if (axios.interceptors.request.handlers.length === 0) axios.interceptors.request.use(resolve, reject)}export const addResponseInterceptor = (resolve, reject) => { if (axios.interceptors.response.handlers.length === 0) axios.interceptors.response.use(resolve, reject)}export const setCookies = Cookies => axios.defaults.headers.cookie = Cookiesexport default httpstore中已经注入到rootState,使用如下:loading({ commit, rootState: { $http } }) { return $http.get(‘path’).then(res => { … }) }在config.js中,把http注册到vue的原型链和配置request、response的拦截器import Vue from ‘vue’// 引入http请求插件import http from ‘./../config/http’// 引入log日志插件import { addRequestInterceptor, addResponseInterceptor } from ‘./../config/http/http’import titleMixin from ‘./utils/title’// 引入log日志插件import vueLogger from ‘./../config/logger/vue-logger’// 注册插件Vue.use(http)Vue.use(vueLogger)Vue.mixin(titleMixin)// request前自动添加api配置addRequestInterceptor( (config) => { /统一加/api前缀/ config.url = /api${config.url} return config }, (error) => { return Promise.reject(error) })// http 返回response前处理addResponseInterceptor( (response) => { /*todo 在这里统一前置处理请求响应 / return Promise.resolve(response.data) }, (error) => { / * todo 统一处理500、400等错误状态 * 这里reject下,交给entry-server.js的处理 */ const { response, request } = error return Promise.reject({ code: response.status, data: response.data, method: request.method, path: request.path }) })这样,.vue中间中直接调用this.$http.get()、this.$http.post()…cookies穿透在ssr期间我们需要截取客户端的cookie,保持用户会话唯一性。在entry-server.js中使用setCookies方法,传入的参数是从context上获取。…… // SSR期间同步cookies setCookies(context.cookies || {})……在vue.koa.ssr.js代码中往context注入cookie…… const context = { url: ctx.url, title: ‘Vue Koa2 SSR’, cookies: ctx.request.headers.cookie }……其他title处理参考官方用到全局变量的第三方插件、组件如何处理等等流式渲染预渲染……还有很多优化、深坑,看看官方文档、踩踩就知道了Koa官方使用express框架。express虽然现在也支持async、await,不过独爱koa。koa主文件// 引入相关包和中间件等等const Koa = require(‘koa’)…const appConfig = require(’./../app.config’)const uri = http://${currentIP}:${appConfig.appPort}// koa serverconst app = new Koa()// 定义中间件,const middleWares = [ ……]middleWares.forEach((middleware) => { if (!middleware) { return } app.use(middleware)})// vue ssr处理vueKoaSSR(app, uri)// http代理中间件app.use(proxyMiddleWare())console.log(\n&gt; Starting server... ${uri} \n)// 错误处理app.on(’error’, (err) => { // console.error(‘Server error: \n%s\n%s ‘, err.stack || ‘’)})app.listen(appConfig.appPort)vue.koa.ssr.jsvue koa2 ssr中间件开发模式直接使用setup.dev.server.jswebpack hot热更新生产模块直接读取dist目录的文件路由匹配匹配proxy代理配置,接口请求进入proxyMiddleWare.js接口代理中间件非接口进入render(),返回htmlconst fs = require(‘fs’)const path = require(‘path’)const LRU = require(’lru-cache’)const { createBundleRenderer } = require(‘vue-server-renderer’)const isProd = process.env.NODE_ENV === ‘production’const proxyConfig = require(’./../app.config’).proxyconst setUpDevServer = require(’./setup.dev.server’)module.exports = function (app, uri) { const renderData = (ctx, renderer) => { const context = { url: ctx.url, title: ‘Vue Koa2 SSR’, cookies: ctx.request.headers.cookie } return new Promise((resolve, reject) => { renderer.renderToString(context, (err, html) => { if (err) { return reject(err) } resolve(html) }) }) } function createRenderer(bundle, options) { return createBundleRenderer(bundle, Object.assign(options, { cache: LRU({ max: 1000, maxAge: 1000 * 60 * 15 }), runInNewContext: false })) } function resolve(dir) { return path.resolve(process.cwd(), dir) } let renderer if (isProd) { // prod mode const template = fs.readFileSync(resolve(‘dist/index.html’), ‘utf-8’) const bundle = require(resolve(‘dist/vue-ssr-server-bundle.json’)) const clientManifest = require(resolve(‘dist/vue-ssr-client-manifest.json’)) renderer = createRenderer(bundle, { template, clientManifest }) } else { // dev mode setUpDevServer(app, uri, (bundle, options) => { try { renderer = createRenderer(bundle, options) } catch (e) { console.log(’\nbundle error’, e) } } ) } app.use(async (ctx, next) => { if (!renderer) { ctx.type = ‘html’ return ctx.body = ‘waiting for compilation… refresh in a moment.’; } if (Object.keys(proxyConfig).findIndex(vl => ctx.url.startsWith(vl)) > -1) { return next() } let html, status try { status = 200 html = await renderData(ctx, renderer) } catch (e) { console.log(’\ne’, e) if (e.code === 404) { status = 404 html = ‘404 | Not Found’ } else { status = 500 html = ‘500 | Internal Server Error’ } } ctx.type = ‘html’ ctx.status = status ? status : ctx.status ctx.body = html })}setup.dev.server.jskoa2的webpack热更新配置和相关中间件的代码,这里就不贴出来了,和express略有区别。部署Pm2简介PM2是node进程管理工具,可以利用它来简化很多node应用管理的繁琐任务,如性能监控、自动重启、负载均衡等,而且使用非常简单。pm2.config.js配置如下module.exports = { apps: [{ name: ‘ml-app’, // app名称 script: ‘config/index.js’, // 要运行的脚本的路径。 args: ‘’, // 由传递给脚本的参数组成的字符串或字符串数组。 output: ‘./log/out.log’, error: ‘./log/error.log’, log: ‘./log/combined.outerr.log’, merge_logs: true, // 集群的所有实例的日志文件合并 log_date_format: “DD-MM-YYYY”, instances: 4, // 进程数 1、数字 2、‘max’根据cpu内核数 max_memory_restart: ‘1G’, // 当内存超过1024M时自动重启 watching: true, env_test: { NODE_ENV: ‘production’ }, env_production: { NODE_ENV: ‘production’ } }],}构建生产代码npm run build 构建生产代码pm2启动服务初次启动pm2 start pm2.config.js –env production # production 对应 env_productionorpm2 start ml-apppm2的用法和参数说明可以参考pm2.md,也可参考PM2实用入门指南Nginx在pm2基础上,Nginx配置upstream实现负载均衡在http节点下,加入upstream节点。upstream server_name { server 172.16.119.198:8018 max_fails=2 fail_timeout=30s; server 172.16.119.198:8019 max_fails=2 fail_timeout=30s; server 172.16.119.198:8020 max_fails=2 fail_timeout=30s; …..}将server节点下的location节点中的proxy_pass配置为:http:// + server_name,即“ http://server_name”.location / { proxy_pass http://server_name; proxy_set_header Host localhost; proxy_set_header X-Forwarded-For $remote_addr}详细配置参考文档如果应用服务是域名子路径ssr的话,需要注意如下location除了需要设置匹配/ssr规则之外,还需设置接口、资源的前缀比如(/api,/dist) location ~ /(ssr|api|dist) {…}vue的路由也该设置base:’/ssr’entry-server.js里router.push(url)这里,url应该把/ssr去掉,即router.push(url.replace(’/ssr’,’’’))参考文档vue官方文档koanginxpm2Demo地址 服务器带宽垃圾,将就看看。 git仓库地址还有很多不足,后续慢慢折腾….结束语:生命的价值在于瞎折腾 ...

January 17, 2019 · 6 min · jiezi

webpack4 单独抽离打包 css 的新实现

webpack4 单独抽离打包 css 的新实现前言之前我们使用的打包 css 无非两种方式:① 将 css 代码打包进 入口 js 文件中;② 使用第三方插件(extract-text-webpack-plugin)实现【注意,该插件在 webpack4 中已经不推荐使用,而且会出现各种莫名其妙的 bug】正是基于对以上两种方式缺点的思考,结合我的实际使用过程,我认为以后我们应该完全摒弃掉上述两种方式,这里推荐一种一种新的实现方式:file-loaderfile-loader我先给个 file-loader 的使用说明吧(传送门:https://github.com/webpack-co…;在我们的传统认知中 file-loader 大多是用来处理 图像元素的,其实如果你认真看过上面的那个传送门的话,才明白我们一直以来是被被误导了,下面我给出几个官方的使用例子吧传统的处理图像module.exports = { module: { rules: [ { test: /.(png|jpg|gif)$/, use: [ { loader: ‘file-loader’, options: {}, }, ], }, ], },};处理 css 【本文重点】const path = require(‘path’);const CleanWebpackPlugin = require(‘clean-webpack-plugin’);const HtmlWebpackPlugin = require(‘html-webpack-plugin’);module.exports = { entry: { app: ‘./src1/index.js’, print: ‘./src1/print.js’ }, output: { filename: ‘[name].bundle.js’, path: path.resolve(__dirname, ‘dist’) }, module: { rules: [ { // loader 图片 test: /.(png|svg|jpg|gif)$/, use: [‘file-loader’] }, { // 处理字体 test: /.(woff|woff2|eot|ttf|otf)$/, use: [ ‘file-loader’ ] }, { // 单独打包出 css test: /.css$/, use: [‘file-loader’] } ] }, plugins: [ new CleanWebpackPlugin([‘dist’]), new HtmlWebpackPlugin({ title: ‘webpack4 extract css methord of new’, template: ‘./src1/index.html’, filename: ‘index.html’, minify: { collapseWhitespace: true }, hash: true }) ], mode: “production”};上面这个代码是我亲自实测过的,大家可以放心借鉴,使用了 file-loader 来处理 css 后,就不再需要额外的插件了,当然了,如果你需要指定输出的 css 文件名和路径,可以这么写 user: [‘file-loader?name=[name].bundle[hash].css’]等,这里的写法就是类似 get 方式的传参那样,?符号后面带参数名即可仅供参考,如果你有什么更好的建议可以 contact 我哦 ...

January 11, 2019 · 1 min · jiezi

技本功丨create-react-app升级webpack4填坑

都说create-react-app是业界最优秀的 React 应用开发工具之一。But,webpack4都更新到v4.20.2 它居然还没升级,简直不能忍看到webpack这更新速度,本人慌得一批,刚好抽空搭建react-andt-mobx脚手架准备升级So,在此分享一下升级攻略,收好不谢!01 安装npm install -g create-react-app02 创建应用//create-react-app是全局命令来创建react项目create-react-app react-demo03 自定义webpack配置npm run eject //自定义模式,暴露出webpack配置,不可逆04 着手自定义webpack配置1、目标结构当然webpack升级准备,调整create-react-app的目录结构已符合我们项目开发的规范是必不可少的。这里重点需关注的为build目录下的一下文件:paths文件更改打包路经更改:在项目开发的过程中host配置以及proxy代理是常见的配置,在create-react-app中配置在package.json配置下,灵活性相对不太好,提取webpack中server.js配置:别忘了修改webpackDevServer.config.js下引用host及proxy下的引用哦。此时,目录改造全部完毕渐入佳境,赶紧进入正题2、webpack3升级webpack4webpack4新出了一个mode模式,有三种选择,none,development,production.最直观的感受就是你可以少些很多配置,因为一旦你开启了mode模式,webpack4就会给你设置很多基本的东西。development模式下,将侧重于功能调试和优化开发体验,包含如下内容:浏览器调试工具开发阶段的详细错误日志和提示快速和优化的增量构建机制production模式下,将侧重于模块体积优化和线上部署,包含如下内容:开启所有的优化代码更小的bundle大小去除掉只在开发阶段运行的代码Scope hoisting和Tree-shaking自动启用uglifyjs对代码进行压缩话不多说,下安装:yarn add webpack webpack-cli webpack-dev-server这3个包是webpack4的基础功能webpack 在 webpack 4 里将命令行相关的都迁移至 webpack-cli 包webpack-dev-server为实时监控文件变化包安装完成之后,请保持淡定,得先运行一下,万一直接能打包呢或许它偷偷做了兼容处理呢,梦想还是要有呢,虽然…Plugin could not be registered at ‘html-webpack-plugin-before-html-processing’. Hook was not found. BREAKING CHANGE: There need to exist a hook at ’this.hooks’. To create a compatiblity layer for this hook, hook into ’this._pluginCompat’.果然还是熟悉的味道上面这个问题是HtmlWebpackPlugin 和 react-dev-utils/InterpolateHtmlPlugin 先后顺序问题,调整下他们的顺序 new HtmlWebpackPlugin({ inject: true, template: paths.appHtml, chunksSortMode: ’none’, }), new InterpolateHtmlPlugin(env.raw),嗯,不出意外的话,搞定再跑一下代码,这个时候可能就出现了一些百度不到问题,你需要升级各种loader了,less-loader,sass-loader style-loader url-loader具体命令:yarn add less-loader@next 和上面的命令相同,依次升级,运行代码,查看报错,缺啥补啥,成功的选择,值得拥有….需要升级的有:html-webpack-pluginreact-dev-utils修改代码完整篇 webpack.config.dev.js:const autoprefixer = require(‘autoprefixer’);const path = require(‘path’); const webpack = require(‘webpack’); const HtmlWebpackPlugin = require(‘html-webpack-plugin’); const CaseSensitivePathsPlugin = require(‘case-sensitive-paths-webpack-plugin’); const InterpolateHtmlPlugin = require(‘react-dev-utils/InterpolateHtmlPlugin’); const WatchMissingNodeModulesPlugin = require(‘react-dev-utils/WatchMissingNodeModulesPlugin’); const eslintFormatter = require(‘react-dev-utils/eslintFormatter’); const ModuleScopePlugin = require(‘react-dev-utils/ModuleScopePlugin’); const getClientEnvironment = require(’./env’); const paths = require(’./paths’); function resolve (dir) { return path.join(__dirname, ‘..’, dir) } const publicPath = ‘/’; const publicUrl = ‘’; const env = getClientEnvironment(publicUrl); module.exports = { mode: ‘development’, devtool: ‘cheap-module-source-map’, entry: [ require.resolve(’./polyfills’), require.resolve(‘react-dev-utils/webpackHotDevClient’), paths.appIndexJs, ], output: { pathinfo: true, filename: ‘static/js/bundle.js’, chunkFilename: ‘static/js/[name].chunk.js’, publicPath: publicPath, devtoolModuleFilenameTemplate: info => path.resolve(info.absoluteResourcePath).replace(//g, ‘/’), }, resolve: { modules: [’node_modules’, paths.appNodeModules].concat( process.env.NODE_PATH.split(path.delimiter).filter(Boolean) ), extensions: [’.web.js’, ‘.mjs’, ‘.js’, ‘.json’, ‘.web.jsx’, ‘.jsx’,’.less’,’.scss’], alias: { ’@’: resolve(‘src’), ‘public’: resolve(‘src/public’), ‘components’: resolve(‘src/components’), ‘pages’: resolve(‘src/pages’), ‘api’: resolve(‘src/api’), ‘mock’: resolve(‘src/public/mock’), }, plugins: [ new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), ], }, module: { strictExportPresence: true, rules: [ { test: /.(js|jsx|mjs)$/, enforce: ‘pre’, use: [ { options: { formatter: eslintFormatter, eslintPath: require.resolve(’eslint’), }, loader: require.resolve(’eslint-loader’), }, ], include: paths.appSrc, }, { oneOf: [ { test: [/.bmp$/, /.gif$/, /.jpe?g$/, /.png$/], loader: require.resolve(‘url-loader’), options: { limit: 10000, name: ‘static/media/[name].[hash:8].[ext]’, }, }, { test: /.(js|jsx|mjs)$/, include: paths.appSrc, loader: require.resolve(‘babel-loader’), options: { cacheDirectory: true, } }, { test: /.(css|less)$/, use: [ require.resolve(‘style-loader’), { loader: require.resolve(‘css-loader’), options: { importLoaders: 1, }, }, { loader: require.resolve(‘postcss-loader’), options: { ident: ‘postcss’, plugins: () => [ require(‘postcss-flexbugs-fixes’), autoprefixer({ browsers: [ ’>1%’, ’last 4 versions’, ‘Firefox ESR’, ’not ie < 9’, // React doesn’t support IE8 anyway ], flexbox: ’no-2009’, }), ], }, }, { loader: require.resolve(’less-loader’) // compiles Less to CSS }, ], }, { test: /.(css|scss)$/, use: [ require.resolve(‘style-loader’), { loader: require.resolve(‘css-loader’), options: { importLoaders: 1, }, }, { loader: require.resolve(‘postcss-loader’), options: { ident: ‘postcss’, plugins: () => [ require(‘postcss-flexbugs-fixes’), autoprefixer({ browsers: [ ’>1%’, ’last 4 versions’, ‘Firefox ESR’, ’not ie < 9’, // React doesn’t support IE8 anyway ], flexbox: ’no-2009’, }), ], }, }, { loader: require.resolve(‘sass-loader’) // compiles Less to CSS }, ], }, { exclude: [/.(js|jsx|mjs)$/,/.(css|less)$/, /.html$/, /.json$/], loader: require.resolve(‘file-loader’), options: { name: ‘static/media/[name].[hash:8].[ext]’, }, }, ], }, ], }, plugins: [ new HtmlWebpackPlugin({ inject: true, template: paths.appHtml, chunksSortMode: ’none’, }), new InterpolateHtmlPlugin(env.raw), new webpack.DefinePlugin(env.stringified), new webpack.HotModuleReplacementPlugin(), new CaseSensitivePathsPlugin(), new WatchMissingNodeModulesPlugin(paths.appNodeModules), new webpack.IgnorePlugin(/^./locale$/, /moment$/), ], node: { dgram: ’empty’, fs: ’empty’, net: ’empty’, tls: ’empty’, child_process: ’empty’, }, performance: { hints: false, }, optimization: { namedModules: true, nodeEnv: ‘development’, }, };还需注意的是webpack4对ExtractTextWebpackPlugin做了调整,建议选用新的CSS文件提取插件mini-css-extract-plugin。生产环境下我们需要做一下配置调整:webpack.config.prod.jsconst autoprefixer = require(‘autoprefixer’);const path = require(‘path’);const webpack = require(‘webpack’);const HtmlWebpackPlugin = require(‘html-webpack-plugin’);const ManifestPlugin = require(‘webpack-manifest-plugin’);const InterpolateHtmlPlugin = require(‘react-dev-utils/InterpolateHtmlPlugin’);const SWPrecacheWebpackPlugin = require(‘sw-precache-webpack-plugin’);const eslintFormatter = require(‘react-dev-utils/eslintFormatter’);const ModuleScopePlugin = require(‘react-dev-utils/ModuleScopePlugin’);const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’);const UglifyJsPlugin = require(“uglifyjs-webpack-plugin”);const OptimizeCSSAssetsPlugin = require(“optimize-css-assets-webpack-plugin”);const paths = require(’./paths’);const getClientEnvironment = require(’./env’);const theme = require(’../antd-theme.js’);function resolve (dir) { return path.join(dirname, ‘..’, dir)}const publicPath = paths.servedPath;const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== ‘false’;const publicUrl = publicPath.slice(0, -1);const env = getClientEnvironment(publicUrl);if (env.stringified[‘process.env’].NODE_ENV !== ‘“production”’) { throw new Error(‘Production builds must have NODE_ENV=production.’);}module.exports = { mode: “production”, bail: true, devtool: shouldUseSourceMap ? ‘source-map’ : false, entry: [require.resolve(’./polyfills’), paths.appIndexJs], output: { path: paths.appBuild, filename: ‘static/js/[name].[chunkhash:8].js’, chunkFilename: ‘static/js/[name].[chunkhash:8].chunk.js’, publicPath: publicPath, devtoolModuleFilenameTemplate: info => path .relative(paths.appSrc, info.absoluteResourcePath) .replace(//g, ‘/’), }, resolve: { modules: [’node_modules’, paths.appNodeModules].concat( // It is guaranteed to exist because we tweak it in env.js process.env.NODE_PATH.split(path.delimiter).filter(Boolean) ), extensions: [’.web.js’, ‘.mjs’, ‘.js’, ‘.json’, ‘.web.jsx’, ‘.jsx’,’.less’], alias: { ’@’: resolve(‘src’), ‘public’: resolve(‘src/public’), ‘components’: resolve(‘src/components’), ‘pages’: resolve(‘src/pages’), ‘mock’: resolve(‘src/public/mock’), ‘api’: resolve(‘src/api’), ‘react-native’: ‘react-native-web’, }, plugins: [ new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), ], }, module: { strictExportPresence: true, rules: [ { test: /.(js|jsx|mjs)$/, enforce: ‘pre’, use: [ { options: { formatter: eslintFormatter, eslintPath: require.resolve(’eslint’), }, loader: require.resolve(’eslint-loader’), }, ], include: paths.appSrc, }, { oneOf: [ { test: [/.bmp$/, /.gif$/, /.jpe?g$/, /.png$/], loader: require.resolve(‘url-loader’), options: { limit: 10000, name: ‘static/media/[name].[hash:8].[ext]’, }, }, // Process JS with Babel. { test: /.(js|jsx|mjs)$/, include: paths.appSrc, loader: require.resolve(‘babel-loader’), options: { plugins: [ [‘import’, [{ libraryName: ‘antd’, style: true }]], // import less ], compact: true, }, }, { test: /.(less|css)$/, use: [ MiniCssExtractPlugin.loader, “css-loader”, “less-loader?{modifyVars:” + JSON.stringify(theme) + “}” ], }, { test: /.(scss|sass)$/, use: [ MiniCssExtractPlugin.loader, “css-loader”, “sass-loader” ] }, { loader: require.resolve(‘file-loader’), exclude: [/.(js|jsx|mjs)$/,/.(css|less)$/, /.html$/, /.json$/], options: { name: ‘static/media/[name].[hash:8].[ext]’, }, }, ], }, ], }, optimization: { runtimeChunk: { name: ‘manifest’ }, minimize: true, noEmitOnErrors: true, minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }), new OptimizeCSSAssetsPlugin({}) ], splitChunks: { minSize: 30000, maxSize: 3000000, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, name: true, cacheGroups: { vendor: { chunks: ‘initial’, name: ‘vendor’, test: ‘vendor’ }, echarts: { chunks: ‘all’, name: ’echarts’, test: /[/]echarts[/]/, } } } }, plugins: [ new HtmlWebpackPlugin({ inject: true, template: paths.appHtml, minify: { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true, }, }), new InterpolateHtmlPlugin(env.raw), new webpack.DefinePlugin(env.stringified), new webpack.NamedModulesPlugin(), new webpack.optimize.OccurrenceOrderPlugin(true), new MiniCssExtractPlugin({ filename: “css/[name].[hash].css”, chunkFilename: “css/[name].[hash].css” }), new ManifestPlugin({ fileName: ‘asset-manifest.json’, }), new SWPrecacheWebpackPlugin({ dontCacheBustUrlsMatching: /.w{8}./, filename: ‘service-worker.js’, logger(message) { if (message.indexOf(‘Total precache size is’) === 0) { return; } if (message.indexOf(‘Skipping static resource’) === 0) { return; } console.log(message); }, minify: true, navigateFallback: publicUrl + ‘/index.html’, navigateFallbackWhitelist: [/^(?!/).*/], staticFileGlobsIgnorePatterns: [/.map$/, /asset-manifest.json$/], }), new webpack.IgnorePlugin(/^./locale$/, /moment$/), ], node: { dgram: ’empty’, fs: ’empty’, net: ’empty’, tls: ’empty’, child_process: ’empty’, },};此时基本完成了webpack4升级的改造但是运行下,还有个隐形的坑待处理……因为引入了andt组件库,高版本对less编译会出错。。噗升级了一路,碰到个需要降级处理的,好,降低less版本 “less”: “2.7.3”,运行一波,亲测完美最后附package.json{ “dependencies”: { “antd”: “^3.9.0-beta.6”, “autoprefixer”: “7.1.6”, “axios”: “^0.18.0”, “babel-core”: “6.26.0”, “babel-eslint”: “7.2.3”, “babel-jest”: “20.0.3”, “babel-loader”: “7.1.2”, “babel-plugin-import”: “^1.8.0”, “babel-polyfill”: “^6.26.0”, “babel-preset-react-app”: “^3.1.1”, “babel-runtime”: “^6.26.0”, “case-sensitive-paths-webpack-plugin”: “2.1.1”, “chalk”: “1.1.3”, “classnames”: “^2.2.6”, “core-decorators”: “^0.20.0”, “create-keyframe-animation”: “^0.1.0”, “css-loader”: “0.28.7”, “dotenv”: “4.0.0”, “dotenv-expand”: “4.2.0”, “eslint”: “4.10.0”, “eslint-config-react-app”: “^2.1.0”, “eslint-loader”: “^2.1.1”, “eslint-plugin-flowtype”: “2.39.1”, “eslint-plugin-import”: “2.8.0”, “eslint-plugin-jsx-a11y”: “5.1.1”, “eslint-plugin-react”: “7.4.0”, “express”: “^4.16.3”, “fastclick”: “^1.0.6”, “file-loader”: “2.0.0”, “fs-extra”: “3.0.1”, “good-storage”: “^1.0.1”, “history”: “^4.7.2”, “html-webpack-plugin”: “^3.2.0”, “immutable”: “^3.8.2”, “jest”: “20.0.4”, “js-base64”: “^2.4.3”, “jsonp”: “^0.2.1”, “less”: “2.7.3”, “less-loader”: “^4.0.1”, “lyric-parser”: “^1.0.1”, “mini-css-extract-plugin”: “^0.4.3”, “mobx”: “^4.1.1”, “mobx-react”: “^5.0.0”, “mobx-react-devtools”: “^5.0.1”, “node-sass”: “^4.9.3”, “object-assign”: “4.1.1”, “optimize-css-assets-webpack-plugin”: “^5.0.1”, “postcss-flexbugs-fixes”: “3.2.0”, “postcss-loader”: “2.0.8”, “promise”: “8.0.1”, “prop-types”: “^15.6.1”, “raf”: “3.4.0”, “react”: “^16.3.0”, “react-addons-css-transition-group”: “^15.6.2”, “react-dev-utils”: “^6.0.0-next.a671462c”, “react-dom”: “^16.3.0”, “react-hot-loader”: “^4.3.4”, “react-lazyload”: “^2.3.0”, “react-loadable”: “^5.5.0”, “react-router-dom”: “^4.2.2”, “react-transition-group”: “^2.3.1”, “sass-loader”: “^7.1.0”, “style-loader”: “0.19.0”, “sw-precache-webpack-plugin”: “^0.11.5”, “uglifyjs-webpack-plugin”: “^2.0.1”, “url-loader”: “0.6.2”, “webpack”: “^4.19.0”, “webpack-cli”: “^3.1.0”, “webpack-dev-server”: “^3.1.8”, “webpack-manifest-plugin”: “^2.0.4”, “whatwg-fetch”: “2.0.3” }, “scripts”: { “start”: “node scripts/start.js”, “build”: “node scripts/build.js” }, “babel”: { “presets”: [ “react-app” ], “plugins”: [ “transform-decorators-legacy” ] }, “eslintConfig”: { “extends”: “react-app” }, “devDependencies”: { “babel-plugin-transform-decorators-legacy”: “^1.3.4”, “better-scroll”: “^1.9.1” },}结 语前端的框架更新速度,悄无声息又超乎想象,需要不断保持着对前端的热情和主动,研究一波前沿的技术架构和设计理念….比如参加D2前端技术沙龙恍惚中,感觉这一波操作好像还能再优化优化 ...

January 10, 2019 · 6 min · jiezi

构建多页面应用——优化(二)

最近,一直尝试使用webpack做多页面应用的开发。并且一个实际的项目为原型,实现对一个静态的企业站进行优化。原站点地址,测试站点地址。如果想要做一个自己个个人博客,或者企业官网来说,有一定的参考意义。webpack的resolve.alias在做模块化开发的过程中,有一个需要解决的问题就是引用模块的路径问题。注:在webpack中,每一个文件(不管是js,css, html,还是图片等)都被称之为一个块。为了实现模块化,细粒度化的控制,往往会将代码块分成为不可分割的块,这样做虽然方便了管理控制,但是也会造成项目的文件嵌套很严重,再饮用的时候需要格外小心路径,同时也会造成开发者的负担(抛开其他不讲,但从技术角度来说,对于开发人员来说,能用一行代码解决的问题,绝不用两行,能少输入一个单词就少输入一个)。而webpack的resolve.alias可以为指定路径的字符串起别名。在本文所使用的示例,这样定义别名:…resolve: { alias: { ‘@’: path.join(__dirname, ‘..’, ‘pages/’), // 根目录 ‘@css’: path.join(__dirname, ‘assets/css/’), // css ‘@img’: path.join(__dirname, ‘assets/imgs/’), // picture // ‘@font’: path.join(__dirname, ‘assets/fonts/’), ‘@data’: path.join(__dirname, ‘pages/data/’), // mock data ‘@utils’: path.join(__dirname, ‘pages/utils/’) // snippets code }},…当然,上面的别名并不是万能的,有一个问题就是background-iamge 和 font-face 的使用url()会有一些问题,url()中的路径必须是字符串,暂时没有好的办法解决。但是使用sass,可以定义变量,可以通过变量来指定路径,但是要严格控制引用变量模块的文件的目录,在本文所使用的示例中,统一将应用变量文件assets/css/path.scss的文件,控制在两个层级。具体可参考所提供源代码中的具体使用。模拟数据实际的项目没有使用任何一种语言的后端代码,更不用说数据库。全部使用的是模拟数据。为了方便管理维护项目的模拟数据,将项目的所有数据统一整理到了示例的pages/data目录下。静态资源图片的处理第一优化的时候,就简单的讲了下,如何使用imagemin提供的插件,来实现对常见类型(.jpg,.png,.gif)图片的处理。第一种引用图片的方案之前做单页面应用开发的时候,喜欢将所有的图片优化处理后统一放在一个目录中,然后将它们放在服务器中,最后在开发或生产环境中,使用绝对路径进行访问。这种方式的好处是不用担心相对路径造成的路径问题。但是缺点是,操作起来不方便,尤其是开发环境。因为你不知道项目究竟要使用多少的静态资源,尤其是使用哪种静态资源。这种方式在团队合作的项目中,比较常见,但是对于提升团队的效率并不明显。第二种引用图片的方案所以,对于开发者来说,如果如果需要什么静态资源,就放在自己的本地目录,这样可以随心所欲的添加。在本文所采用的示例中,我做了一些尝试,将所有的图片资源进行了分类。需要转化为base64的图片放一个文件夹assets/imgs/base64/,需要合成雪碧图的单独放在一个文件夹;assets/imgs/sprites/,为了方便管理合成不同雪碧图的源图片,我又在该目录下创建了子文件夹;而对于<img src="…" />要引用的图片的存放使用了两个文件夹,assets/imgs/static存放了未经优化的所有的图片,而目录assets/imgs/others,存放了所有优化过的图片(包含两部分,一部分是使用npm run img命令优化的assets/imgs/static目录下的图片,另一部分是npm run dev命令优化的雪碧图图片,它的前缀带有-sprite这样的后缀)。这种方案,使用的是相对路径应用图片。可参考pages/data/contactus.js文件的代码:const loadImg = require(’@utils/load-img’)module.exports = { cn_name: ‘联系我们’, en_name: ‘CONTACT US’, img: loadImg(‘second/contactus-tag.png’), …}而工具代码片段loadImg的代码如下:module.exports = function(str) { return require(’@img/other/’ + str)}源代码webpack4.x multi-page构建多页面应用系列文章构建多页面应用构建多页面应用——单个页面的处理构建多页面应用——模板构建多页面应用——静态资源构建多页面应用——优化(一)构建多页面应用——hash构建多页面应用——优化(二)

January 7, 2019 · 1 min · jiezi

构建多页面应用——hash

这里的hash主要从两个方面来说。一个是webpack生成的hash,另一个是页面链接中的hash,如:http://localhost:8080/ywbk.html#restaurant中的#restaurant。后者在单页面应用的路由处理中经常用到。webpack中的hashwebapck每次构建都会生成一个新的hash(主要用于生产环境)。它的作用就是用来标记构建生成的状态,通常使用的过程中,会将它注入到构建输出(生成)的文件名中。在webpack.config.js文件中的位置如下:…output: { path: path.resolve(__dirname, ‘dist/’), filename: isDev ? ‘assets/js/[name].js’ : ‘assets/js/[name].[contenthash].js’, publicPath: isDev ? config.devUrl : config.deployUrl},module: { rules: [ … { include: path.resolve(__dirname, ‘assets/imgs/other/’), test: /.(png|jpe?g|gif)$/, use: [ { loader: ‘file-loader’, options: { name: isDev ? ‘[name].[ext]’ : ‘[name].[hash].[ext]’, outputPath: ‘assets/imgs/’ } } ] } … ]}…plugins: plugins.concat([ new MiniCssExtractPlugin({ filename: isDev ? ‘assets/css/[name].css’ : ‘assets/css/[name].[contenthash].css’, chunkFilename: isDev ? ‘assets/css/[id].css’ : ‘assets/css/[id].[contenthash].css’ }) …])如果项目是第一次构建,那么所有的静态资源都会被添加同一个hash,当第二构建如果有内容改变,构建生成的文件会被赋予新的hash。为了提高见构建的速度,减少构建生成不必要的文件(如果第一次生成的文件和第二次生成的文件内容相同,那么没必要重新在生成一次)。为此webpack提供了三个hash字段可供选择,分别是hash、chunkhash、contenthash。hash:不管内容改变与否,所有的文件都会被重新生成一遍。这是一种粗放的模式。chunkhash:它主要针对与webpack配置文件的entry中定义的入口文件。contenthash: 它主要针对的是webpack构建的过程中,提取(或者说分离)出来的内容,如:extract-text-webpack-plugin针对多页面应用的构建特点,使用contenthash是一个不错的选择,本文中所使用的示例wepbakck4.x-multi-page符合这个特点。css的分离,js文件的分离(webpack4的SplitChunk)。为什么要在多页面应用中使用单页面应用的hash来实现路由的控制演示效果很明显的一点是传统的多页面应用的业务模块往往会出现多个页面之间会有很多相同内容,这样在单击导航实现路由切换的时候,总是会看到相同的内容,这样会给用户造成一种错觉‘为什么总是同一个页面’。这样的用户体验往往不好。最突出的就是包含二级导航的页面。(可参考圣捷集团的官网)单页面应用给我们了一个很好的启示,可以通过将这些页面结构相似的,而只有一部分内容类同的页面组合成为一个页面。这样做的好处显而易见,减少多页面构建生成的页面数量,我们之构建生成一级和二级页面,以及一些页面结构很少雷同的页面,而不构建生成三级页面。优化后的示例地址具体的实现有两种解决方案。第一种,在每个页面中使用一个vue(结合vue-router)的示例(也可以使用,react,angular)。第二种,自己实现对不同hash的处理。注:如果使用hash,在开发的时候一定要模拟一个服务器环境,直接用浏览器打开是无法实现的,浏览器控制台会提示跨域的错误。本文所使用的示例用的是第二种方案。具体的实现过程如下:在生成子导航的模拟数据中添加了一个type值,tabs: [ { cn_name: ‘圣捷投资’, en_name: ‘SHENGJIE INVESTMENT’, type: ‘investment’ }, { cn_name: ‘董事长致辞’, en_name: ‘CHAIRMANS SPEECH’, type: ‘speech’ } …]这样使用pug-loader处理生成的html对应的元素中会包含一个data-type自定义属性。参考代码如下:<div class=“tabs”> <div class=“tab-item active” data-type=“finance”> <div>互联网金融</div> <div>ONLINE FINANCE</div> </div> <div class=“tab-item” data-type=“allfinance”> <div>全品类金融</div> <div>WHOLE CATEGORY FINANCE</div> </div> …</div>然后,使用JavaScript通过控制触发条件,如url的hash改变,进而控制页面的展示效果。参考代码如下:···$(’.tab-item’).on(‘click’, function() { var type = $(this).data(’type’) window.location.hash = type tab(this, type)})···首先,单点击二级导航时,改变url的hash。这样做可以让用户通过操作浏览器的前进和后退按钮来控制页面,此外使用浏览器的前进和后退按钮的好处是,浏览可以记录页面的状态。(只用上面的代码无法实现想要的效果)其次,使用hashchange这个浏览器自带的监听hash改变的api(他兼容>=ie8的浏览器,所以可以放心使用)。···$(window).on(‘hashchange’, function() { tabcheck()})···通过它们,就可以轻松的实现url的hash。为了页面呈现更好的效果,可以给页面添加一个滚动的动画,如果不使用hash在传统的页面中实现有些棘手。那么针对页面底部的网站导航,如何结合hash来操作页面并实现一致的路由切换效果呢?这里需要监听页面的load状态,在webpack中,使用commonjs来组织js代码块,需要注意window.load(…)无效的情况。具体的实现就不一一介绍了,可参考demo中tabs.js文件的代码源代码webpack4.x multi-page构建多页面应用系列文章构建多页面应用构建多页面应用——单个页面的处理构建多页面应用——模板构建多页面应用——静态资源构建多页面应用——优化(一) ...

January 6, 2019 · 1 min · jiezi

构建多页面应用——优化(一)

构建多页面应用的过程中,需要优化代码的结构。而优化代码的结构,往往会使用函数化编程。可参考webpack4.x-demo。在进行多页面应用编程的过程中,webpack.config.js的代码规模会随着项目规模的增加而增加。而造成webpack.config.js文件增大的原因,主要是从entry,plugins的配置,而造成plugins增加是html-webpack-plugin和webpack-spritesmith的使用。因为每增加一个页面,就需要增加一个entry和实例化的html-wepback-plguin,而每增加一个雪碧图就要使用一个webpack-spritesimth。为了更好的展示多页面应用的开发,我将近期优化的一个项目的部分代码整理了一下。演示地址,源代码参考文章结尾提供的地址。项目结构的改动为了方便发布,需要调整一下项目的目录结构。需要把assets文件夹提取到根目录下,这个文件包含了项目所有的资源文件,如果放在src目录下,会造成路径问题,如:图片、字体。如果不调整,会造成在开发的模式下可以访问到的路径,而在生产模式下不能访问。所以,为了保证开发模式和生产模式下静态资源的路径一致,需要这样的调整。注:如果用vue做过开发的话,相信你就会明白,使用vue-cli生成的项目,在根目录下有一个static文件夹,它用来保存项目开发的过程中引用到的所有的静态资源,而本文中介绍的示例中的assets文件夹有类似的作用,这个文件夹后续还会继续优化。entry首先,为了简化开发中的重复定义,添加了pages/utils/nav.js和pages/utils/subnav.js文件。它们用来存放项目各页面的路由和seo优化所需要的信息。代表每个页面的结构如下:[ { href: ‘/’, text: ‘首页’, name: ‘index’, meta: { ‘description’: ‘这是首页’, ‘keywords’: ‘webpack, multi-page, 首页’, ‘author’: ‘https://github.com/lvzhenbang/’ } …]然后,根据需要新添加了一个multipage.config.js文件,在这里定义了如何生成一个entry,代码如下:const path = require(‘path’)let navs = require(’./pages/utils/nav’)const subnavs = require(’./pages/utils/subnav’)navs = navs.concat(subnavs)// entrylet entry = {}for (let nav of navs) { entry[nav.name] = ‘./pages/’ + nav.name + ‘.js’}// commons css/jsentry.other = [’./pages/utils/commons.js’, ‘./pages/utils/css.js’]pluginsplugins 包含两部分。第一部分,html-wepback-plugin,修改multipage.config 代码如下:···// make pagesconst HtmlWebapckPlugin = require(‘html-webpack-plugin’);let plugins = []for (let nav of navs) { plugins.push(new HtmlWebapckPlugin({ /* inital page / filename: nav.name + ‘.html’, chunks: [nav.name, ‘other’], / page head / title: nav.text, meta: nav.meta, favicon: path.resolve(__dirname, ‘assets/favicon.jpg’), template: path.resolve(__dirname, ‘pages/’ + nav.name + ‘.pug’), minify: true }))}第二部分,webpack-spritesimth生成雪碧图,修改multipage.config.js,代码如下:…// sprites const SpritesmithPlugin = require(‘webpack-spritesmith’);const sprites = require(’./pages/utils/sprites’)for(let sprite of sprites) { plugins.push(new SpritesmithPlugin({ src: { cwd: path.resolve(__dirname, ‘assets/imgs/sprites/’ + sprite + ‘/’), glob: ‘.png’ }, target: { image: path.resolve(__dirname, ‘assets/imgs/other/’ + sprite + ‘-sprite.png’), css: path.resolve(__dirname, ‘assets/css/’ + sprite + ‘/’ + sprite + ‘-sprite.scss’) }, apiOptions: { cssImageRef: ‘../../imgs/other/’ + sprite + ‘-sprite.png’ } }))}根据项目需要,需添加pages/utils/sprites.js文件,它用来保存要合成的雪碧图的名字,这个名字也是合成雪碧图所需的图片所在的文件夹名字。这个文件比较简单,用来导出一个字符串数组。注:至于为什么只用一个字符串数组,构建多页面应用——静态资源这篇文章可以说明。引用multipage.config.js在webapck.config.js文件中引用multipage.config.js,代码如下:…const multipage = require(’./multipage.config’)let entry = multipage.entry, plugins = multipage.pluginsmodule.exports = (mode) => { … return { entry: entry, … plugins: plugins.concat([ new MiniCssExtractPlugin({ filename: isDev ? ‘assets/css/[name].css’ : ‘assets/css/[name].[contenthash].css’, chunkFilename: isDev ? ‘assets/css/[id].css’ : ‘assets/css/[id].[contenthash].css’ }), new CopyWebpackPlugin([ { from: path.resolve(__dirname, ‘assets/imgs/other/’), to: path.resolve(__dirname, ‘dist/assets/imgs/other/’), ignore: [’.*’] } ]) ]) }}当然,你可以考虑使用webpack-merge。源代码webpack4.x multi-page构建多页面应用系列文章构建多页面应用构建多页面应用——单个页面的处理构建多页面应用——模板构建多页面应用——静态资源 ...

January 5, 2019 · 1 min · jiezi

[ webpack4 ] 配置属于自己的打包系统教程(最终篇)—— 环境配置篇

GitHub 完整配置文件地址: https://github.com/yhtx1997/w…由于篇幅过长分三次发布,建议按顺序看[ webpack4 ] 配置属于自己的打包系统教程(一)—— 基础配置篇[ webpack4 ] 配置属于自己的打包系统教程(二)—— 资源配置篇[ webpack4 ] 配置属于自己的打包系统教程(最终篇)—— 环境配置篇环境配置篇主要内容开发环境生产环境分离实时预览,热更新webpack-dev-server 其他常用代码复用处理开发环境和生产环境到了这一步,该讲讲开发环境模式和生产环境模式了开发环境是自己开发时用的,需要有实时编译功能、模块热替换功能(更新文件不用完全更新页面)、错误提示到具体哪个文件几行生产环境是放到线上给用户使用的,需要代码压缩功能配置代码组件化我们先把之前配置好的 webpack 配置文件改下名,改名 webpack.common.js ,意思是开发环境和生产环境都需要的,将代码压缩之类的挪到生产配置下之后安装 webpack-merge ,官方推荐的是为每个环境写单独的 webpack 文件虽然有简单的方法实现但是依然推荐写单独的配置文件,因为在这样的配置方式你可以更清楚你自己在做什么,还可以让你的配置更加个性(自定义)安装 webpack-mergenpm install webpack-merge -D新建开发环境配置我们新建一个 js 文件,命名为 webpack.dev.js ,添加如下代码const merge = require(‘webpack-merge’);//合并配置const common = require(’./webpack.common.js’);//引入公共配置 module.exports = merge(common, { mode: ‘development’,//声明是开发环境 //关于 dev 的配置 })新建开发环境配置我们新建一个 js 文件,命名为 webpack.prod.js 添加如下代码const merge = require(‘webpack-merge’);//合并配置const UglifyJSPlugin = require(‘uglifyjs-webpack-plugin’);//用来压缩 js 代码const common = require(’./webpack.common.js’);//引入公共配置module.exports = merge(common, { mode: ‘production’,//声明是生产环境 //关于 prod 的配置});开发环境实时预览跟着配置并操作的小伙伴可能发现了,每次修改后都需要手动在命令行输入命令,并且还要刷新浏览器才能看到最新的效果 那么现在来解决这两个问题,方法就是使用 webpack-dev-server安装npm install webpack-dev-server -D配置实时预览模块热更新自定义请求代理自定义 ip 及端口注:使用 webpack-dev-server 并不会编译到本地文件,而是放到内存中const merge = require(‘webpack-merge’);const common = require(’./webpack.common.js’);const path = require(‘path’);const webpack = require(‘webpack’); module.exports = merge(common, { mode: ‘development’, plugins: [ new webpack.NamedModulesPlugin(),//模块热更新 new webpack.HotModuleReplacementPlugin()//模块热更新 ], devServer: { contentBase: path.join(__dirname, ‘dist’),//预览的目录,写出口目录的绝对路径 hot: true,//模块热更新 host: ’localhost’,//默认值 也可以改为 127.0.0.1 或者其他 port: 8080, proxy: { ‘/api’: ‘http://localhost:3000’//请求到 /api/users 现在会被代理到请求 http://localhost:3000/api/users } } })接下来加入错误提示以及将生成 HTML 的代码从公共配置( webpack.common.js )拿到这里 因为开发和生产有些许不一样,我又不知道怎样简单配置,所以开发环境和生产环境我都会放一个生成 HTML 的代码生产环境每次打包都要清理掉旧文件所有代码都要进行压缩重复的 js 代码,需要只有一个就好文件清理从公共配置( webpack.common.js )将之前文件清理的代码拿过来放到这里const merge = require(‘webpack-merge’);const common = require(’./webpack.common.js’);const CleanWebpackPlugin = require(‘clean-webpack-plugin’);module.exports = merge(common, { mode: ‘production’, plugins: [ new CleanWebpackPlugin([‘dist’]), new HtmlWebpackPlugin({ title: ‘2048’, template: ‘./src/index.html’, minify: true,//HTML 代码压缩 hash: true }), ],})代码压缩先安装压缩代码的插件npm install uglifyjs-webpack-plugin -D npm install optimize-css-assets-webpack-plugin -D uglifyjs-webpack-plugin 是 js 压缩插件optimize-css-assets-webpack-plugin 是推荐和const UglifyJsPlugin = require(“uglifyjs-webpack-plugin”);const OptimizeCSSAssetsPlugin = require(“optimize-css-assets-webpack-plugin”);复用代码分离假设需要使用 jQuery 来辅助开发,我在 a.js b.js 两个文件都引入了 jQuery ,将 a.js b.js 打包成 a.min.js b.min.js ,这时看他们的体积会比原来大很多,且 a 和 b 的代码中都包含完整的 jQuery 代码为了解决这种情况,我们需要将 jQuery 这种复用的代码分离到单独的文件在将环境设置为生产环境时默认开启了很多功能,其中 SplitChunksPlugin 就是用于避免重复依赖的在我们不配置时 默认配置是这样的 optimization: { splitChunks: { chunks: ‘async’, minSize: 30000, minChunks: 1, } }chunks: 表示哪些代码需要优化,有三个可选值:initial(初始块)、async(按需加载块)、all(全部块),默认为async 改成 all 支持所有的minSize: 这个文件最少是多少才去优化,默认为 30000 实际测试是文件大于 30 kb,在 31kb时开始优化minChunks: 最少引用几次才去优化,默认为1 实际测试为在只引用一次的情况不优化,只有大于它才优化注: 还有其他属性个人感觉不常用就没写,了解更多可以看这里最终代码汇总最终公共配置 webpack.common.js 代码如下const path = require(‘path’);const MiniCssExtractPlugin = require(“mini-css-extract-plugin”);module.exports = { entry: { 512:’./src/js/512.js’, 1024:’./src/js/1024.js’, 2048:’./src/js/2048.js’, }, plugins: [ new MiniCssExtractPlugin({ filename: “css/[name].css” }) ], output: { filename: “js/[name].js”, path: path.resolve(__dirname, ‘dist’) }, module: { rules: [ { test: /.js$/, exclude: /(node_modules|bower_components)/, use: { loader: ‘babel-loader’, options: { presets: [’@babel/preset-env’] } } }, { test: /.scss$/, use: [ MiniCssExtractPlugin.loader, ‘css-loader’, ‘postcss-loader’, ‘sass-loader’, ] }, { test: /.(png|svg|jpg|gif)$/, use: [ ‘file-loader’ ] }, { test: /.(woff|woff2|eot|ttf|otf)$/, use: [ ‘file-loader’ ] }, { test: /.(csv|tsv)$/, use: [ ‘csv-loader’ ] }, { test: /.xml$/, use: [ ‘xml-loader’ ] } ] },}最终开发环境 webpack.dev.js 代码如下const merge = require(‘webpack-merge’);const common = require(’./webpack.common.js’);const path = require(‘path’);const webpack = require(‘webpack’);const HtmlWebpackPlugin = require(‘html-webpack-plugin’); module.exports = merge(common, { mode: ‘development’, plugins: [ new HtmlWebpackPlugin({ title: ‘2048’, template: ‘./src/index.html’, minify: false, hash: true }), new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin() ], devtool: ‘inline-source-map’, devServer: { contentBase: path.join(__dirname, ‘dist’), hot: true, host: ’localhost’, port: 8080, proxy: { ‘/api’: ‘http://localhost:3000’ } } })最终生产环境 webpack.prod.js 代码如下const merge = require(‘webpack-merge’);const common = require(’./webpack.common.js’);const UglifyJsPlugin = require(“uglifyjs-webpack-plugin”);const OptimizeCSSAssetsPlugin = require(“optimize-css-assets-webpack-plugin”);const HtmlWebpackPlugin = require(‘html-webpack-plugin’);const CleanWebpackPlugin = require(‘clean-webpack-plugin’);module.exports = merge(common, { mode: ‘production’, plugins: [ new CleanWebpackPlugin([‘dist’]), new HtmlWebpackPlugin({ title: ‘2048’, template: ‘./src/index.html’, minify: true, hash: true }), ], optimization: { splitChunks: { chunks: ‘all’ }, minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: true }), new OptimizeCSSAssetsPlugin({}) ], }});npm 项目配置代码 package.json 代码如下{ “name”: “2048”, “version”: “1.0.0”, “description”: “”, “private”: true, “main”: “index.js”, “scripts”: { “dev”: “webpack-dev-server –open –config webpack.dev.js”, “build”: “webpack –config webpack.prod.js” }, “keywords”: [], “author”: “”, “license”: “ISC”, “dependencies”: { “webpack”: “^4.28.3” }, “devDependencies”: { “@babel/core”: “^7.2.2”, “@babel/preset-env”: “^7.2.3”, “autoprefixer”: “^9.4.3”, “babel-loader”: “^8.0.4”, “clean-webpack-plugin”: “^1.0.0”, “css-loader”: “^2.1.0”, “csv-loader”: “^3.0.2”, “extract-text-webpack-plugin”: “^4.0.0-beta.0”, “file-loader”: “^3.0.1”, “html-webpack-plugin”: “^3.2.0”, “mini-css-extract-plugin”: “^0.5.0”, “node-sass”: “^4.11.0”, “optimize-css-assets-webpack-plugin”: “^5.0.1”, “postcss”: “^7.0.7”, “postcss-loader”: “^3.0.0”, “sass-loader”: “^7.1.0”, “style-loader”: “^0.23.1”, “uglifyjs-webpack-plugin”: “^2.1.1”, “webpack-cli”: “^3.1.2”, “webpack-dev-server”: “^3.1.14”, “webpack-merge”: “^4.1.5”, “xml-loader”: “^1.2.1” }} ...

January 3, 2019 · 3 min · jiezi

[ webpack4 ] 配置属于自己的打包系统教程(二)—— 资源配置篇

GitHub 完整配置文件地址: https://github.com/yhtx1997/webpack4-Instance 由于篇幅过长分三次发布,建议按顺序看[ webpack4 ] 配置属于自己的打包系统教程(一)—— 基础配置篇[ webpack4 ] 配置属于自己的打包系统教程(二)—— 资源配置篇[ webpack4 ] 配置属于自己的打包系统教程(最终篇)—— 环境配置篇资源配置篇资源配置篇ES6 -> ES5提取 css 到单独文件css 浏览器兼容前缀补全css 代码压缩使用 sass使用 HTML 模板清理旧的打包文件静态资源加载与解析通过下面的配置 可以在 js 里引入相应的文件,然后进行解析 也可以直接解析相应的文件配置 babel 将 ES6 转换为兼容性语法(低版本语法 ES5 或 ES3)安装 babel-loadernpm install -D babel-loader @babel/core @babel/preset-env babel-loader:使用 Babel 转换 JavaScript 依赖关系的 Webpack 加载器@babel/core: 将 ES6 代码转换为 ES5@babel/preset-env: 决定使用哪些 api 为旧浏览器提供现代浏览器的新特性module: { rules: [ { test: /.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: ‘babel-loader’, options: { presets: [’@babel/preset-env’] } } } ]}加载 css安装提取 css 相关的 npm 包npm install style-loader css-loader -D提取 css 相关配置const path = require(‘path’);module.exports = { entry: { 2048: ‘./src/js/2048.js’, 1024: ‘./src/js/1024.js’, 512: ‘./src/js/512.js’ }, output: { filename: “[name].js”, path: path.resolve(__dirname, ‘dist’) }, module: { rules: [ { test: /.css$/, //匹配所有以 .css 为后缀的文件 use: [//使用以下loader来加载 ‘style-loader’, ‘css-loader’ ] } ] }}安装 sass开发 css 现在多数使用 sass 和 lass ,所以配置下 sass 相应的安装 lass 只需要把 sass-loader 切换为 less-loadernpm install sass-loader node-sass -D配置{ test: /.scss$/, use: [ “style-loader”, “css-loader”, “sass-loader” ]}CSS 分离成文件方案一 安装 extract-text-webpack-plugin方案一简单写下,推荐方案二npm install extract-text-webpack-plugin -Dextract-text-webpack-plugin 提取 css 到单独文件配置const ExtractTextPlugin = require(“extract-text-webpack-plugin”);plugins: [ new CleanWebpackPlugin([‘dist’]), new HtmlWebpackPlugin({ title:‘2048’, template: ‘./src/index.html’, minify:true, hash:true }), new ExtractTextPlugin({ filename: ‘css/[name].css’ }),],module: { rules: [ { test: /.scss$/, use: ExtractTextPlugin.extract({ fallback: “style-loader”, use: [“css-loader”,“sass-loader”] }) }, ]}方案二 安装 MiniCssExtractPlugin 推荐与extract-text-webpack-plugin相比异步加载没有重复的编译(性能)更容易使用特定于CSSnpm install mini-css-extract-plugin postcss-loader autoprefixer postcss optimize-css-assets-webpack-plugin -Dmini-css-extract-plugin 提取 css 到单独文件autoprefixer 浏览器兼容前缀补全(例如 -webkit-)optimize-css-assets-webpack-plugin 代码压缩配置const MiniCssExtractPlugin = require(“mini-css-extract-plugin”);const OptimizeCSSAssetsPlugin = require(“optimize-css-assets-webpack-plugin”);optimization: { minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }), new OptimizeCSSAssetsPlugin({}) ] },plugins: [ new CleanWebpackPlugin([‘dist’]), new HtmlWebpackPlugin({ title:‘2048’, template: ‘./src/index.html’, minify:true, hash:true }), new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional filename: “css/[name].css” })],module: { rules: [ { test: /.scss$/, use: [ MiniCssExtractPlugin.loader, ‘css-loader’, ‘postcss-loader’, ‘sass-loader’, ] }, ] }这里需要注意的是需要新建一个 postcss.config.js 文件,用来配置自动加前缀module.exports={ plugins: [ require(‘autoprefixer’)({ /* …options */ }) ]}加载数据数据文件包括 JSON 文件,CSV、TSV 和 XML JSON 默认就是支持的,所以如果需要处理剩下的使用下面的方法就可以了安装提取 数据 相关的 npm 包npm install csv-loader xml-loader -D安装提取 数据 相关的 npm 包{ test: /.(csv|tsv)$/, use: [ ‘csv-loader’ ]},{ test: /.xml$/, use: [ ‘xml-loader’ ]}加载其他资源加载其他静态资源都可以使用 file-loader 来加载npm install file-loader -D加载图片{ test: /.(png|svg|jpg|gif)$/, use: [ ‘file-loader’ ]}加载字体{ test: /.(woff|woff2|eot|ttf|otf)$/, use: [ ‘file-loader’ ]}设定 HtmlWebpackPlugin当我们真正应用我们写的代码时,需要我们新建 HTML ,并且需要我们手动的在 HTML 里引入,使用 HtmlWebpackPlugin 可以让我们不用每次都新建 HTML 以及 手动去引入我们的代码 它会帮我们每次运行 webpack 时新建一个 HTML 并引入所有打包好的 js css安装npm install html-webpack-plugin -D配置 HTML 模板const HtmlWebpackPlugin = require(‘html-webpack-plugin’);//引入HtmlWebpackPlugin//官网是将其放到了入口 entry 与出口 output 之间plugins: [ new HtmlWebpackPlugin({ title: ‘Output Management’,//表示 HTML title 标签的内容 template: ‘./src/index.html’,//表示模板路径 minify: true,//压缩代码 hash: true//加上哈希值来达到去缓存的目的 })]清理 ./dist 文件夹如果我们使用了哈希值来命名我们的文件,那么每次更该内容都会生成新的文件,同时旧的文件依然存在,这样的话一个是乱,一个是浪费 我们可以使用 CleanWebpackPlugin 在每次打包时都会将之前的旧文件清除掉安装npm install clean-webpack-plugin -D配置const CleanWebpackPlugin = require(‘clean-webpack-plugin’);plugins: [ new CleanWebpackPlugin([‘dist’]),//删除dist new HtmlWebpackPlugin({ title: ‘Output Management’,//表示 HTML title 标签的内容 template: ‘./src/index.html’,//表示模板路径 minify: true,//压缩代码 hash: true//加上哈希值来达到去缓存的目的 })] ...

January 3, 2019 · 2 min · jiezi

[ webpack4 ] 配置属于自己的打包系统教程(一)—— 基础配置篇

GitHub 完整配置文件地址: https://github.com/yhtx1997/webpack4-Instance 由于篇幅过长分三次发布,建议按顺序看[ webpack4 ] 配置属于自己的打包系统教程(一)—— 基础配置篇[ webpack4 ] 配置属于自己的打包系统教程(二)—— 资源配置篇[ webpack4 ] 配置属于自己的打包系统教程(最终篇)—— 环境配置篇基础配置篇主要内容安装 webpack4目录初始化配置初始化入口及多入口配置出口配置安装 webpack4安装 webpack 前请确保已安装 nodejs 和 npm新建 npm 项目npm init -y这里的init表示初始化一个 npm 项目, -y 表示全部选 yes,不加的话会提示输入一些项目信息,比如项目名,版本号,作者…安装 webpack4npm install webpack webpack-cli webpack-dev-server -D这其实是一条合并的命令,拆开就是npm install webpack -Dnpm install webpack-cli -Dnpm install webpack-dev-server -Dinstall 是安装的意思; -D 表示安装到本地开发环境,不使用全局安装是因为每个项目可能用的 webpack 版本不一样导致冲突第一条安装的是 webpack 的核心文件,就好比是安装包第二条是让 webpack 支持类似 npm run dev 这种命令行命令第三条安装的是可以使 webpack 支持实时编译的拓展包初始化 npm 配置及文件现阶段的目标是让它能运行起来 现在目录下应该有一个文件夹,两个 .josn 文件node_modules 用来存放所有安装的 npm 包package.json 在这里配置 npm run 的脚本,以及包含项目信息,安装了哪些包package-lock 详细的包的版本来源,确保项目所有开发人员用的都是一个版本调整 package.json官方推荐移除文件中的入口配置,这样可以防止意外发布你的代码。“main”: “index.js"并加上私有属性配置"private”: true运行webpack 可以用在命令行输入 npx webpack 但是这样的运行方式在配置了开发环境和生产环境时,再运行需要写很多参数 所以添加一个 npm 脚本,之后运行 输入 npm run test 即可,有参数后在后边追加即可 “test”: “webpack"最后看起来像这样新建入口文件及生成出口文件在 webpack 4 中,可以无须任何配置使用,做完上边的操作后可以在命令行输入 npm run test 运行 webpack(这里的 test 是在上边 scripts 里自己定义的),但是会显示这样的结果。这是因为入口文件不存在,webpack 默认是将当前目录下的 ./src/index.js 当做要打包的文件(入口),新建一下 src 目录和 index.js 文件,就可以正常了。这时会生成一个 dist 目录以及目录里有一个 main.js,这是默认的打包好的文件及目录(出口),这样一个 webpack 算是初始化完成了。注:最终发布时如果只需要一个 js,那么开发时 index.js 里推荐是只用来引入其他 js 文件(import)。在截止 2018年12月31日 据我所知道的目前 import 和 export 还只是概念上的标准, js 还不能原生支持 import 和 export ,大家能使用是因为配置了 babel ,通过 babel 进行编译,使其变成 node.js 的代码,使其可以将这条命令视为加载模块。 nodejs 采用 CommonJS规范,关于 ES6 这方面的可以看 ES6 Module 的语法。webpack4 入口出口配置在上面也说了 webpack4 现在可以无需使用任何配置文件就可以使用,但是有些东西还是弄成自己喜欢的比较好新建配置文件在当前目录下新建一个 webpack.config.js 文件,并写入代码const path = require(‘path’);//[1]module.exports = { //[2]};[1] 是引入 node 的 path 模块,这样就可以处理文件与目录的路径,处理路径是因为 windows 系列和 Linux 系列在路径的表示上不太一样。[2] 是对外暴露大括号 {} 中的内容,用来写我们自定义的配置注:关于配置文件名,wepack4 默认是会引入 ./webpack.config.js ,如果想自己改名字的话可以在命令行输入一下代码,其中 webpack –config 是必须的, my.config.js 是自己自定义的配置文件的路径webpack –config my.config.js入口配置为了能证明入口确实改了有效果,我将 ./src/index.js 的文件名改为 2048.js,并放到./src/js/2048.js ;并修改代码const path = require(‘path’);module.exports = { entry: “./src/2048.js”//add};在不加上面代码时会报之前的找不到入口的错误,加了以后会显示正常输出了,并且 ./dis 下会多一个 2048.js 的文件,多入口配置entry 不光能赋值绝对路径的字符串,还能赋值多个路径的数组或对象entry: ‘./src/2048.js’//单入口 字符串传参entry: [’./src/js/2048.js’,’./src/js/1024.js’,’./src/js/512.js’]//多入口 数组传参entry: {//多入口 对象传参 2048: ‘./src/js/2048.js’, 1024: ‘./src/js/1024.js’, 512: ‘./src/js/512.js’ }出口配置出口跟入口不太一样,入口可以有很多,但是只有一个输出配置。output: { filename: ‘2048.js’, path: ‘C:/Users/GengShaojing/Desktop/2048/dist’}filename 打包后文件的文件名。path 打包后文件的的绝对路径。多入口多输出文件output: { filename: ‘[name].js’,//[1] path: path.resolve(__dirname, ‘dist’)//[2]}[1] [name] 表示使用 entry 传递过来的文件的文件名或者对象的 key 值[2] __dirname 指向当前文件(webpack 配置文件)的绝对路径, path.resolve 是解析路径并在路径后加上 dist每次修改后生成不一样的文件名output: { filename: “[name].[chunkhash].js”, path: path.resolve(__dirname, ‘dist’)}filename 支持以下几个属性,且可以共存[name] 模块名称 就是之前说的文件名或者对象的 key 值[id] 模块标识符 应该是入口传入顺序的下标值从 0 开始[hash] 模块标识符的哈希值 这个我理解的不太清楚,只知道他可以生成字符串[chunkhash] 内容的哈希值 根据内容生成字符串[contenthash] 提取的内容生成的哈希值 根据提取的内容生成字符串注:官方推荐 [name] 加上 [chunkhash] 的模式注:哈希值就是用算法提取的标识信息,相当于人和录入指纹,哈希值就是人录入指纹的机器,最后的字符串就是指纹 ...

January 3, 2019 · 2 min · jiezi

构建多页面应用——静态资源的处理

在之前的系列文章中,我已经介绍了如何用webpack实现多页面应用的js,html,css的处理。今天就主要介绍如何处理静态资源,在web开发中最常见的静态资源就是图片。图片的引用方式而因为在web中,图片有两种主要的引入方式,第一种是<img src="…">,第二种是backgorund-image: url(…)。前者在html中使用,后者在css中使用。web开发中的图片处理图片常见的类型有jp(e)g,png,gif,包括现在普遍使用的svg以及webp。svg作为矢量图形,有一定的使用场景,而webp作为未来web开发的趋势,有一定的使用场景,比如:爱奇艺中的轮播图(carousel)中就是用webp,但其他的见到的不多。现在,web开发中使用最多的还是jpg和png,处理他们,在构建工具中使用url-loader和file-loader就好了,其中file-loader负责图片的拷贝和输出,并会给图片名添加一个hash值。说到这里,很多人会想到字体图标。以前处理web页面中的图标(icon),使用图片来处理,这样会带来一个性能问题就是http请求的增多,这样会造成服务器的负载压力,同时会带来用户体验的问题,因为会出现页面的局部空白和页面重绘的问题,当然一种解决方案使雪碧图(sprite),但是如果图片过大怎么解决,如何对图片进行分解(大变小的问题),图片的拼接比较困难,最要命的是在引用雪碧图时要进行计算,除此之外就是如何对雪碧图的组成图片进行自定义的删减,而使用字体图标这些问题,都会得到一定程度的解决,当然在构建工具中可以使用webpack-spritesmith这个插件来处理组成雪碧图的图片。当然,有些特殊的情况,需要使用base64,这里使用url-loader即可。将图片处理为base64有使用场景,将图片转换为雪碧图亦有使用场景,单独的图片处理也有使用场景(这些使用场景的图片大小从左到右依次增大)。这些场景在一个web项目中都会涉及到。虽然使用字体图标可以替代雪碧图,因为字体图标有更小的尺寸,更自由的操作手法(如:图标颜色的自定义),但是一个DIY的web项目还是有些图标还是需要雪碧图。但是,这里有一个问题,如何在一个项目中同时使用base64,雪碧图,字体图标,单独的图片。在构建中如何使用多种图片处理方式在构建多页面应用中,如何解决呢?字体图标处理字体图标很简单,如:iconfont(阿里巴巴字体图标库),就像引用css那么简单。base64base64的处理,使用url-loader。雪碧图雪碧图的处理,可使用webpack-spritesmith这个插件单独的图片使用file-loader,它负责拷贝url-loader的处理结果,并输出。上面就是我们常见的图片处理,如果要处理svg可以参考svg-url-loader,如果要处理webp可以参考webp-loader如何对图片进行优化对图片进行优化,会带来良好的用户体验。熟悉图片优化的都知道渐进式(progressive),可参考nuwen.netjp(e)g可以进行连续性处理,这样可保证图片数据请求回来多少,就渲染多少,是自上而下的渲染,也是有模糊到清晰的状态。png可以进行交叉处理,这样也可保证图片数据请求回来多少,就渲染多少,它是整体的显示,而且是又模糊状态到清晰的状态。gif图片一般使用小图,如果是大图会记号浏览器性能,还不如使用视频,或者用css动画来代替。我个人整理了一个css 动画集,有需要的可以看一下。在构建多页面应用中,会使用到image-webpack-loader来做优化处理。其中,配置项options中的mozjpeg 处理jp(e)g图片,pngquant处理png图片,gifsicle处理gif图片,webp处理webp图片。多页面应用中的图片处理首先,看一下多页面应用中的目录结构图:./src│ aboutUs.js│ contactUs.js│ css.js│ index.js│ recruitment.js│ ├─assets│ │ favicon.jpg│ │ │ ├─css│ │ │ index.scss│ │ │ │ │ ├─commons│ │ │ ├─container│ │ │ │ index.scss│ │ │ │ │ │ │ ├─footer│ │ │ │ index.scss│ │ │ │ │ │ │ └─header│ │ │ index.scss│ │ │ │ │ ├─productus│ │ │ index.scss│ │ │ productus-sprite.scss│ │ │ │ │ └─utils│ │ btn.scss│ │ form.scss│ │ inital.scss│ │ list.scss│ │ modeal.scss│ │ normalize.scss│ │ pagination.scss│ │ popover.scss│ │ table.scss│ │ text.scss│ │ tooltip.scss│ │ │ └─imgs│ ├─base64│ │ fe.jpg│ │ │ ├─other│ │ float.jpg│ │ productus-sprite.png│ │ │ └─sprites│ └─productus│ product-us_01.png│ product-us_02.png│ product-us_03.png│ product-us_04.png│ product-us_05.png│ product-us_06.png│ product-us_07.png│ product-us_08.png│ product-us_09.png│ product-us_10.png│ product-us_11.png│ product-us_12.png│ ├─pages│ │ recruitment.pug│ │ template.pug│ │ │ └─components│ ├─commons│ │ ├─container│ │ │ index.pug│ │ │ │ │ ├─footer│ │ │ index.pug│ │ │ │ │ └─header│ │ index.pug│ │ │ └─productus│ index.pug│ └─utils load.js跟以前的实例代码相比,这次的文件目录结构变化较大,这里将要处理的所有文件模块都放在了src目录下。可能有人会问,为什么要要将目录分的这么细,下面我就说一下为什么这么分。aboutUs.js, contactUs.js, index.js, recruitment.js是四个路由页面,要用到的js代码,css.js处理各个路由页面公用的css代码;静态资源目录(assets)下,存放web项目常用的静态资源;静态资源目录下的css目录统一存放整个web项目所用到的css样式。其中commons存放公用的css模块,每个公用模块有创建一个目录存放该公用模块可以使用到的css模块(提醒,不要分的过于细),而其他的如productus存放产品模块代码,根据开发的需要可以创建其他的模块目录,目录结构类似于commons中的header模块,其中utils放置自己总结的工具模块代码,如table,form等。然后,指定style-loader,css-loader等样式相关的loader处理css样式文件,这样可以减少遍历,缩短构建时间。静态资源目录下的imgs目录,用来存放整个项目中,用到的图片。在这里,分为base64,sprite,ohter等,为什么要这样分?如果分的话file-loader这个webapck的loader会复制并导出imgs目下所有的图片,者在构建中并不是我们需要的,这样会增加构建的时间。如果让url-loader处理base64目录下的图片,file-loader处理ohter目录下的图片,webpack-spritesmith处理sprite目录下的图片,并将生成的图片放到ohter目录下,用file-loader进行二次处理。这样做,webpack处理更精确,可以减少不必要的遍历,极大地减少构建的时间,同样方便对图片的管理,特别是对于需要改变sprite的图片的管理。在imgs目录下创建base64目录,sprite目录,使用file-loader指定处理ohter目录,是为了避免file-loader将所有的图片都拷贝一份并导出到dist输出目录中,因为与base64相关的文件已经在css样式文件中了,再拷贝一份,已经没有意义,而sprite相关的文件会被webpack-spritesmith插件先处理生成一个文件,所以再拷贝它们也没有意义,还会让构建速度更慢。在page目录下,放置所有的html代码块(这里使用pug编译器生成相应的html代码块),它的目录分类和css相类似,它们是一一对应的关系。注意:iamge-webpack-loader,要先对所有的图片进行优化处理,然后再用其他loader处理。loader的执行顺序,如果你是style-loader!css-loader!sass-loader"使用,它是从右到左方向先后执行,如果你是在配置文件中的rules: […]数组中,它也是从右到左的方向执行,如果你将所有的loader规则有回车符号隔开,那么它就是自下而上的执行。一类特殊的图片引用针对<img src="…">的图片使用,ul-loader是不会处理html中的img引用,现在处理这样情况的loader或插件,也并没有一个比较出名的。现在通用的做法就是将图片拷贝一份到生成目录中,copy-webpack-plugin。参考配置代码如下:new CopyWebpackPlugin([ { from: path.resolve(__dirname, ‘assets/imgs/other/’), to: path.resolve(__dirname, ‘dist/assets/imgs/other/’), ignore: [’.’] }]),但这样,会带来另一个问题就是图片的优化问题,如何使用 image-webpack-loader?本来的目的是对项目中使用到的所有的图片进行优化,而现在只能对base64和sprite目录下的图片进行优化处理。不过,不要慌,可以通过创建一个新的npm脚本命令(本是里使用的是npm run img)来对图片进行压缩处理,新建了一个目录static用来保存优化前的图片,ohter用来保存优化后的图片。首先,需要安装imagemin, imagemin-mozjpeg, imagemin-optipng, imagemin-gifsicle :yarn add imagemin imagemin-mozjpeg imagemin-optipng imagemin-gifsicle –dev然后,在项目的根目录添加一个优化图片的文件optzing-img.js,代码如下:const path = require(‘path’)const imagemin = require(‘imagemin’);const imageminMozjpeg = require(‘imagemin-mozjpeg’);const imageminOptipng = require(‘imagemin-optipng’);const imageminGifsicle = require(‘imagemin-gifsicle’);(async () => { await imagemin( [ path.resolve(__dirname, ‘src/assets/imgs/static/.jpg’), path.resolve(__dirname, ‘src/assets/imgs/static/.png’), path.resolve(__dirname, ‘src/assets/imgs/static/.gif’) ], path.resolve(__dirname, ‘src/assets/imgs/other/’), { use: [ imageminMozjpeg(), imageminOptipng(), imageminGifsicle() ] } ); console.log(‘图片优化完成!’);})();最后,在package.json文件中添加如下的npm命令:…“scripts”: { … “img”: “node optzing-img.js” },…在控制输入npm run img,然后按下回车键就可以得到你所需要的。注:既然重新定义了图片优化的npm脚本命令,那么,是否需要去掉之前在webpack.config.js中的image-webpakc-loader,当然不需要。主要有两个原因,一个是sprite雪碧图它是用几张小图片合成了一张大图片,这张合成的图片还需要优化;另一个是因为本项目对于存放图片的目录进行了细化。字体在web开发中,自定义的字体也是比较常见的,在webpack中它的处理和图片类似,都是使用的 url-loader 和 file-loader。参考代码如下:…{ include: path.resolve(__dirname, ‘assets/fonts/’), test: /.(woff2?|eot|ttf|otf)(?.*)?$/, use: [{ loader: ‘url-loader’, options: { limit: 10000, name: isDev ? ‘[name].[ext]’ : ‘[name].[hash].[ext]’, outputPath: ‘assets/fonts/’ } }]},…新创建了一个fonts目录又来存放项目开发过程中使用的字体。源代码webpack4.x multi-page此后,webpack构建多页面应用系列文章的源代码,都在这个github项目中,webpack3.x multi-page不再维护。构建多页面应用系列文章构建多页面应用构建多页面应用——单个页面的处理构建多页面应用——模板构建多页面应用——静态资源 ...

January 1, 2019 · 2 min · jiezi

webpack-chain项目中文翻译

webpack-chain注意:这是对原项目readme文件的翻译,为啥翻译这个呢,因为Vue CLI3脚手架生成的项目使用这种方式配置webpack,但是脚手架中对这块的介绍不多,所以把这部分翻译出来,以供团队和大家参考。应用一个链式 API 来生成和简化 2-4 版本的webpack的配置的修改。此文档对应于webpack-chain的v5版本,对于以前的版本,请参阅:v4 docsv3 docsv2 docsv1 docs注意: 虽然 webpack-chain 被广泛应用在Neutrino中,然而本软件包完全独立,可供任何项目使用。介绍webpack 的核心配置的创建和修改基于一个有潜在难于处理的 JavaScript 对象。虽然这对于配置单个项目来说还是 OK 的,但当你尝试跨项目共享这些对象并使其进行后续的修改就会变的混乱不堪,因为您需要深入了解底层对象的结构以进行这些更改。webpack-chain 尝试通过提供可链式或顺流式的 API 创建和修改webpack 配置。API的 Key 部分可以由用户指定的名称引用,这有助于 跨项目修改配置方式 的标准化。通过以下示例可以更容易地解释这一点。安装webpack-chain 需要 Node.js v6.9及更高版本. webpack-chain 也只创建并被设计于使用webpack的2,3,4版本的配置对象。你可以使用Yarn或者npm来安装此软件包(俩个包管理工具选一个就行):Yarn方式yarn add –dev webpack-chainnpm方式npm install –save-dev webpack-chain入门当你安装了 webpack-chain, 你就可以开始创建一个webpack的配置。 对于本指南,我们的示例基本配置 webpack.config.js 将位于我们项目的根目录。// 导入 webpack-chain 模块,该模块导出了一个用于创建一个webpack配置API的单一构造函数。const Config = require(‘webpack-chain’);// 对该单一构造函数创建一个新的配置实例const config = new Config();// 用链式API改变配置// 每个API的调用都会跟踪对存储配置的更改。config // 修改 entry 配置 .entry(‘index’) .add(‘src/index.js’) .end() // 修改 output 配置 .output .path(‘dist’) .filename(’[name].bundle.js’);// 创建一个具名规则,以后用来修改规则config.module .rule(’lint’) .test(/.js$/) .pre() .include .add(‘src’) .end() // 还可以创建具名use (loaders) .use(’eslint’) .loader(’eslint-loader’) .options({ rules: { semi: ‘off’ } });config.module .rule(‘compile’) .test(/.js$/) .include .add(‘src’) .add(’test’) .end() .use(‘babel’) .loader(‘babel-loader’) .options({ presets: [ [’@babel/preset-env’, { modules: false }] ] });// 也可以创建一个具名的插件!config .plugin(‘clean’) .use(CleanPlugin, [[‘dist’], { root: ‘/dir’ }]);// 导出这个修改完成的要被webpack使用的配置对象module.exports = config.toConfig();共享配置也很简单。仅仅导出配置 和 在传递给webpack之前调用 .toConfig() 方法将配置导出给webpack使用。// webpack.core.jsconst Config = require(‘webpack-chain’);const config = new Config();// 跨目标共享配置// Make configuration shared across targets// …module.exports = config;// webpack.dev.jsconst config = require(’./webpack.core’);// Dev-specific configuration// 开发具体配置// …module.exports = config.toConfig();// webpack.prod.jsconst config = require(’./webpack.core’);// Production-specific configuration// 生产具体配置// …module.exports = config.toConfig();ChainedMapwebpack-chain 中的核心API接口之一是 ChainedMap. 一个 ChainedMap的操作类似于JavaScript Map, 为链式和生成配置提供了一些便利。 如果一个属性被标记一个 ChainedMap, 则它将具有如下的API和方法:除非另有说明,否则这些方法将返回 ChainedMap , 允许链式调用这些方法。// 从 Map 移除所有 配置.clear()// 通过键值从 Map 移除单个配置.// key: *delete(key)// 获取 Map 中相应键的值// key: *// returns: valueget(key)// 获取 Map 中相应键的值// 如果键在Map中不存在,则ChainedMap中该键的值会被配置为fn的返回值.// key: *// fn: Function () -> value// returns: valuegetOrCompute(key, fn)// 配置Map中 已存在的键的值// key: *// value: *set(key, value)// Map中是否存在一个配置值的特定键,返回 真或假// key: *// returns: Booleanhas(key)// 返回 Map中已存储的所有值的数组// returns: Arrayvalues()// 返回Map中全部配置的一个对象, 其中 键是这个对象属性,值是相应键的值,// 如果Map是空,返回 undefined// 使用 .before() 或 .after() 的ChainedMap, 则将按照属性名进行排序。// returns: Object, undefined if emptyentries()// 提供一个对象,这个对象的属性和值将 映射进 Map。// 你也可以提供一个数组作为第二个参数以便忽略合并的属性名称。// obj: Object// omit: Optional Arraymerge(obj, omit)// 对当前配置上下文执行函数。// handler: Function -> ChainedMap // 一个把ChainedMap实例作为单个参数的函数batch(handler)// 条件执行一个函数去继续配置// condition: Boolean// whenTruthy: Function -> ChainedMap // 当条件为真,调用把ChainedMap实例作为单一参数传入的函数// whenFalsy: Optional Function -> ChainedMap // 当条件为假,调用把ChainedMap实例作为单一参数传入的函数when(condition, whenTruthy, whenFalsy)ChainedSetwebpack-chain 中的核心API接口另一个是 ChainedSet. 一个 ChainedSet的操作类似于JavaScript Map, 为链式和生成配置提供了一些便利。 如果一个属性被标记一个 ChainedSet, 则它将具有如下的API和方法:除非另有说明,否则这些方法将返回 ChainedSet , 允许链式调用这些方法。// 添加/追加 给Set末尾位置一个值.// value: *add(value)// 添加 给Set开始位置一个值.// value: prepend(value)// 移除Set中全部值.clear()// 移除Set中一个指定的值.// value: delete(value)// 检测Set中是否存在一个值.// value: // returns: Booleanhas(value)// 返回Set中值的数组.// returns: Arrayvalues()// 连接给定的数组到 Set 尾部。// arr: Arraymerge(arr)// 对当前配置上下文执行函数。// handler: Function -> ChainedSet // 一个把 ChainedSet 实例作为单个参数的函数batch(handler)// 条件执行一个函数去继续配置// condition: Boolean// whenTruthy: Function -> ChainedSet // 当条件为真,调用把 ChainedSet 实例作为单一参数传入的函数// whenFalsy: Optional Function -> ChainedSet // 当条件为假,调用把 ChainedSet 实例作为单一参数传入的函数when(condition, whenTruthy, whenFalsy)速记方法存在许多简写方法,用于 使用与简写方法名称相同的键在 ChainedMap 设置一个值例如, devServer.hot 是一个速记方法, 因此它可以用作:// 在 ChainedMap 上设置一个值的 速记方法devServer.hot(true);// 上述方法等效于:devServer.set(‘hot’, true);一个速记方法是可链式的,因此调用它将返回 原实例,允许你继续链式使用配置创建一个新的配置对象const Config = require(‘webpack-chain’);const config = new Config();移动到API的更深层将改变你正在修改的内容的上下文。 你可以通过 config在此引用顶级配置或者通过调用 .end() 方法向上移动一级 使你移回更高的 上下文环境。如果你熟悉jQuery, 这里与其 .end() 工作原理类似。除非另有说明,否则全部的API调用都将在当前上下文中返回API实例。 这样,你可以根据需要连续 链式API调用. 有关对所有速记和低级房费有效的特定值的详细信息,请参阅 webpack文档层次结构 中的相应名词。Config : ChainedMap配置速记方法config .amd(amd) .bail(bail) .cache(cache) .devtool(devtool) .context(context) .externals(externals) .loader(loader) .mode(mode) .parallelism(parallelism) .profile(profile) .recordsPath(recordsPath) .recordsInputPath(recordsInputPath) .recordsOutputPath(recordsOutputPath) .stats(stats) .target(target) .watch(watch) .watchOptions(watchOptions)配置 entryPoints// 回到 config.entryPoints : ChainedMapconfig.entry(name) : ChainedSetconfig .entry(name) .add(value) .add(value)config .entry(name) .clear()// 用低级别 config.entryPoints:config.entryPoints .get(name) .add(value) .add(value)config.entryPoints .get(name) .clear()配置 output: 速记 方法config.output : ChainedMapconfig.output .auxiliaryComment(auxiliaryComment) .chunkFilename(chunkFilename) .chunkLoadTimeout(chunkLoadTimeout) .crossOriginLoading(crossOriginLoading) .devtoolFallbackModuleFilenameTemplate(devtoolFallbackModuleFilenameTemplate) .devtoolLineToLine(devtoolLineToLine) .devtoolModuleFilenameTemplate(devtoolModuleFilenameTemplate) .filename(filename) .hashFunction(hashFunction) .hashDigest(hashDigest) .hashDigestLength(hashDigestLength) .hashSalt(hashSalt) .hotUpdateChunkFilename(hotUpdateChunkFilename) .hotUpdateFunction(hotUpdateFunction) .hotUpdateMainFilename(hotUpdateMainFilename) .jsonpFunction(jsonpFunction) .library(library) .libraryExport(libraryExport) .libraryTarget(libraryTarget) .path(path) .pathinfo(pathinfo) .publicPath(publicPath) .sourceMapFilename(sourceMapFilename) .sourcePrefix(sourcePrefix) .strictModuleExceptionHandling(strictModuleExceptionHandling) .umdNamedDefine(umdNamedDefine)配置 resolve(解析): 速记方法config.resolve : ChainedMapconfig.resolve .cachePredicate(cachePredicate) .cacheWithContext(cacheWithContext) .enforceExtension(enforceExtension) .enforceModuleExtension(enforceModuleExtension) .unsafeCache(unsafeCache) .symlinks(symlinks)配置 resolve 别名config.resolve.alias : ChainedMapconfig.resolve.alias .set(key, value) .set(key, value) .delete(key) .clear()配置 resolve modulesconfig.resolve.modules : ChainedSetconfig.resolve.modules .add(value) .prepend(value) .clear()配置 resolve aliasFieldsconfig.resolve.aliasFields : ChainedSetconfig.resolve.aliasFields .add(value) .prepend(value) .clear()配置 resolve descriptionFieldsconfig.resolve.descriptionFields : ChainedSetconfig.resolve.descriptionFields .add(value) .prepend(value) .clear()配置 resolve extensionsconfig.resolve.extensions : ChainedSetconfig.resolve.extensions .add(value) .prepend(value) .clear()配置 resolve mainFieldsconfig.resolve.mainFields : ChainedSetconfig.resolve.mainFields .add(value) .prepend(value) .clear()配置 resolve mainFilesconfig.resolve.mainFiles : ChainedSetconfig.resolve.mainFiles .add(value) .prepend(value) .clear()配置 resolveLoader当前API config.resolveLoader 相同于 配置 config.resolve 用下面的配置:配置 resolveLoader moduleExtensionsconfig.resolveLoader.moduleExtensions : ChainedSetconfig.resolveLoader.moduleExtensions .add(value) .prepend(value) .clear()配置 resolveLoader packageMainsconfig.resolveLoader.packageMains : ChainedSetconfig.resolveLoader.packageMains .add(value) .prepend(value) .clear()配置 performance(性能): 速记方法config.performance : ChainedMapconfig.performance .hints(hints) .maxEntrypointSize(maxEntrypointSize) .maxAssetSize(maxAssetSize) .assetFilter(assetFilter)配置 optimizations(优化): 速记方法config.optimization : ChainedMapconfig.optimization .concatenateModules(concatenateModules) .flagIncludedChunks(flagIncludedChunks) .mergeDuplicateChunks(mergeDuplicateChunks) .minimize(minimize) .namedChunks(namedChunks) .namedModules(namedModules) .nodeEnv(nodeEnv) .noEmitOnErrors(noEmitOnErrors) .occurrenceOrder(occurrenceOrder) .portableRecords(portableRecords) .providedExports(providedExports) .removeAvailableModules(removeAvailableModules) .removeEmptyChunks(removeEmptyChunks) .runtimeChunk(runtimeChunk) .sideEffects(sideEffects) .splitChunks(splitChunks) .usedExports(usedExports)配置 optimization minimizers(最小优化器)// 回到 config.optimization.minimizersconfig.optimization .minimizer(name) : ChainedMap配置 optimization minimizers: 添加注意: 不要用 new 去创建最小优化器插件,因为已经为你做好了。config.optimization .minimizer(name) .use(WebpackPlugin, args)// 例如config.optimization .minimizer(‘css’) .use(OptimizeCSSAssetsPlugin, [{ cssProcessorOptions: { safe: true } }])// Minimizer 插件也可以由它们的路径指定,从而允许在不使用插件或webpack配置的情况下跳过昂贵的 require s。config.optimization .minimizer(‘css’) .use(require.resolve(‘optimize-css-assets-webpack-plugin’), [{ cssProcessorOptions: { safe: true } }])配置 optimization minimizers: 修改参数config.optimization .minimizer(name) .tap(args => newArgs)// 例如config .minimizer(‘css’) .tap(args => […args, { cssProcessorOptions: { safe: false } }])配置 optimization minimizers: 修改实例config.optimization .minimizer(name) .init((Plugin, args) => new Plugin(…args));配置 optimization minimizers: 移除config.optimization.minimizers.delete(name)配置插件// 回到 config.pluginsconfig.plugin(name) : ChainedMap配置插件: 添加注意: 不要用 new 去创建插件,因为已经为你做好了。config .plugin(name) .use(WebpackPlugin, args)// 例如config .plugin(‘hot’) .use(webpack.HotModuleReplacementPlugin);// 插件也可以由它们的路径指定,从而允许在不使用插件或webpack配置的情况下跳过昂贵的 require s。config .plugin(’env’) .use(require.resolve(‘webpack/lib/EnvironmentPlugin’), [{ ‘VAR’: false }]);配置插件: 修改参数config .plugin(name) .tap(args => newArgs)// 例如config .plugin(’env’) .tap(args => […args, ‘SECRET_KEY’]);配置插件: 修改实例config .plugin(name) .init((Plugin, args) => new Plugin(…args));配置插件: 移除config.plugins.delete(name)配置插件: 在之前调用指定当前插件上下文应该在另一个指定插件之前执行,你不能在同一个插件上同时使用 .before() 和 .after()。config .plugin(name) .before(otherName)// 例如config .plugin(‘html-template’) .use(HtmlWebpackTemplate) .end() .plugin(‘script-ext’) .use(ScriptExtWebpackPlugin) .before(‘html-template’);Config plugins: 在之后调用指定当前插件上下文应该在另一个指定插件之后执行,你不能在同一个插件上同时使用 .before() 和 .after()。config .plugin(name) .after(otherName)// 例如config .plugin(‘html-template’) .after(‘script-ext’) .use(HtmlWebpackTemplate) .end() .plugin(‘script-ext’) .use(ScriptExtWebpackPlugin);配置 resolve 插件// 回到 config.resolve.pluginsconfig.resolve.plugin(name) : ChainedMap配置 resolve 插件: 添加注意: 不要用 new 去创建插件,因为已经为你做好了。config.resolve .plugin(name) .use(WebpackPlugin, args)配置 resolve 插件: 修改参数config.resolve .plugin(name) .tap(args => newArgs)配置 resolve 插件: 修改实例config.resolve .plugin(name) .init((Plugin, args) => new Plugin(…args))配置 resolve 插件: 移除config.resolve.plugins.delete(name)配置 resolve 插件: 在之前调用指定当前插件上下文应该在另一个指定插件之前执行,你不能在同一个插件上同时使用 .before() 和 .after()。config.resolve .plugin(name) .before(otherName)// 例如config.resolve .plugin(‘beta’) .use(BetaWebpackPlugin) .end() .plugin(‘alpha’) .use(AlphaWebpackPlugin) .before(‘beta’);配置 resolve 插件: 在之后调用指定当前插件上下文应该在另一个指定插件之后执行,你不能在同一个插件上同时使用 .before() 和 .after()。config.resolve .plugin(name) .after(otherName)// 例如config.resolve .plugin(‘beta’) .after(‘alpha’) .use(BetaWebpackTemplate) .end() .plugin(‘alpha’) .use(AlphaWebpackPlugin);配置 nodeconfig.node : ChainedMapconfig.node .set(’__dirname’, ‘mock’) .set(’__filename’, ‘mock’);配置 devServerconfig.devServer : ChainedMap配置 devServer allowedHostsconfig.devServer.allowedHosts : ChainedSetconfig.devServer.allowedHosts .add(value) .prepend(value) .clear()配置 devServer: 速记方法config.devServer .bonjour(bonjour) .clientLogLevel(clientLogLevel) .color(color) .compress(compress) .contentBase(contentBase) .disableHostCheck(disableHostCheck) .filename(filename) .headers(headers) .historyApiFallback(historyApiFallback) .host(host) .hot(hot) .hotOnly(hotOnly) .https(https) .inline(inline) .info(info) .lazy(lazy) .noInfo(noInfo) .open(open) .openPage(openPage) .overlay(overlay) .pfx(pfx) .pfxPassphrase(pfxPassphrase) .port(port) .progress(progress) .proxy(proxy) .public(public) .publicPath(publicPath) .quiet(quiet) .setup(setup) .socket(socket) .staticOptions(staticOptions) .stats(stats) .stdin(stdin) .useLocalIp(useLocalIp) .watchContentBase(watchContentBase) .watchOptions(watchOptions)配置 moduleconfig.module : ChainedMap配置 module: 速记方法config.module : ChainedMapconfig.module .noParse(noParse)配置 module rules: 速记方法config.module.rules : ChainedMapconfig.module .rule(name) .test(test) .pre() .post() .enforce(preOrPost)配置 module rules uses (loaders): 创建config.module.rules{}.uses : ChainedMapconfig.module .rule(name) .use(name) .loader(loader) .options(options)// Exampleconfig.module .rule(‘compile’) .use(‘babel’) .loader(‘babel-loader’) .options({ presets: [’@babel/preset-env’] });配置 module rules uses (loaders): 修改选项config.module .rule(name) .use(name) .tap(options => newOptions)// 例如config.module .rule(‘compile’) .use(‘babel’) .tap(options => merge(options, { plugins: [’@babel/plugin-proposal-class-properties’] }));配置 module rules oneOfs (条件 rules)config.module.rules{}.oneOfs : ChainedMap<Rule>config.module .rule(name) .oneOf(name)// 例如config.module .rule(‘css’) .oneOf(‘inline’) .resourceQuery(/inline/) .use(‘url’) .loader(‘url-loader’) .end() .end() .oneOf(’external’) .resourceQuery(/external/) .use(‘file’) .loader(‘file-loader’)合并配置webpack-chain 支持将对象合并到配置实例,改实例类似于 webpack-chain 模式 布局的布局。 请注意,这不是 webpack 配置对象,但您可以再将webpack配置对象提供给webpack-chain 以匹配器布局之前对其进行转换。config.merge({ devtool: ‘source-map’ });config.get(‘devtool’) // “source-map"config.merge({ [key]: value, amd, bail, cache, context, devtool, externals, loader, mode, parallelism, profile, recordsPath, recordsInputPath, recordsOutputPath, stats, target, watch, watchOptions, entry: { [name]: […values] }, plugin: { [name]: { plugin: WebpackPlugin, args: […args], before, after } }, devServer: { [key]: value, clientLogLevel, compress, contentBase, filename, headers, historyApiFallback, host, hot, hotOnly, https, inline, lazy, noInfo, overlay, port, proxy, quiet, setup, stats, watchContentBase }, node: { [key]: value }, optimizations: { concatenateModules, flagIncludedChunks, mergeDuplicateChunks, minimize, minimizer, namedChunks, namedModules, nodeEnv, noEmitOnErrors, occurrenceOrder, portableRecords, providedExports, removeAvailableModules, removeEmptyChunks, runtimeChunk, sideEffects, splitChunks, usedExports, }, performance: { [key]: value, hints, maxEntrypointSize, maxAssetSize, assetFilter }, resolve: { [key]: value, alias: { [key]: value }, aliasFields: […values], descriptionFields: […values], extensions: […values], mainFields: […values], mainFiles: […values], modules: […values], plugin: { [name]: { plugin: WebpackPlugin, args: […args], before, after } } }, resolveLoader: { [key]: value, alias: { [key]: value }, aliasFields: […values], descriptionFields: […values], extensions: […values], mainFields: […values], mainFiles: […values], modules: […values], moduleExtensions: […values], packageMains: […values], plugin: { [name]: { plugin: WebpackPlugin, args: […args], before, after } } }, module: { [key]: value, rule: { [name]: { [key]: value, enforce, issuer, parser, resource, resourceQuery, test, include: […paths], exclude: […paths], oneOf: { [name]: Rule }, use: { [name]: { loader: LoaderString, options: LoaderOptions, before, after } } } } }})条件配置当使用的情况下工作ChainedMap和ChainedSet,则可以使用执行条件的配置when。您必须指定一个表达式 when(),以评估其真实性或虚假性。如果表达式是真实的,则将使用当前链接实例的实例调用第一个函数参数。您可以选择提供在条件为假时调用的第二个函数,该函数也是当前链接的实例。// 示例:仅在生产期间添加minify插件config .when(process.env.NODE_ENV === ‘production’, config => { config .plugin(‘minify’) .use(BabiliWebpackPlugin); });// 例:只有在生产过程中添加缩小插件,否则设置devtool到源映射config .when(process.env.NODE_ENV === ‘production’, config => config.plugin(‘minify’).use(BabiliWebpackPlugin), config => config.devtool(‘source-map’) );检查生成的配置您可以使用检查生成的webpack配置config.toString()。这将生成配置的字符串化版本,其中包含命名规则,用法和插件的注释提示:config .module .rule(‘compile’) .test(/.js$/) .use(‘babel’) .loader(‘babel-loader’);config.toString();{ module: { rules: [ / config.module.rule(‘compile’) / { test: /.js$/, use: [ / config.module.rule(‘compile’).use(‘babel’) / { loader: ‘babel-loader’ } ] } ] }}默认情况下,如果生成的字符串包含需要的函数和插件,则不能直接用作真正的webpack配置。为了生成可用的配置,您可以通过__expression在其上设置特殊属性来自定义函数和插件的字符串化方式:class MyPlugin {}MyPlugin.__expression = require('my-plugin');function myFunction () {}myFunction.__expression = require('my-function');config .plugin(’example’) .use(MyPlugin, [{ fn: myFunction }]);config.toString();/{ plugins: [ new (require(‘my-plugin’))({ fn: require(‘my-function’) }) ]}/通过其路径指定的插件将require()自动生成其语句:config .plugin(’env’) .use(require.resolve(‘webpack/lib/ProvidePlugin’), [{ jQuery: ‘jquery’ }])config.toString();{ plugins: [ new (require(’/foo/bar/src/node_modules/webpack/lib/EnvironmentPlugin.js’))( { jQuery: ‘jquery’ } ) ]}您还可以调用toString静态方法Config,以便在字符串化之前修改配置对象。Config.toString({ …config.toConfig(), module: { defaultRules: [ { use: [ { loader: ‘banner-loader’, options: { prefix: ‘banner-prefix.txt’ }, }, ], }, ], },}){ plugins: [ / config.plugin(‘foo’) */ new TestPlugin() ], module: { defaultRules: [ { use: [ { loader: ‘banner-loader’, options: { prefix: ‘banner-prefix.txt’ } } ] } ] }} ...

December 28, 2018 · 6 min · jiezi

构建多页面应用——模板

因为大多数人都比较喜欢,或者说倾向于用js操作现有的html代码块,而不喜欢用js来生成html代码块,之后再来操作它。很明显的一点儿就是前者清晰明了,后者不是那么直观。因此在开发中,我们会接触到模板后者模板引擎这样概念。我们比较常见的就是*.html模板,Java开发中的*.jsp,php开发中的*.php,还有用于node.js的*.ejs 和 .jade(以及它的最新版本.pug)。这里,着重说一下html和pug。如何使用html-webpack-plugin的模板和注意事项html-webpack-plugin 支持为生成的页面指定模板,我们可以直接使用配置项为template,那么这个指定的html模板应该如何操作,或者说应该怎么操作,才能达到灵活多变的特性。使用webapcK做多页面应用的构建,我们当然是希望它能够实现构建单页面应用那样的模块化处理。我们从构建建多页面应用知道了构建多页面应用,可以实现js代码的模块化,从构建多页面应用——单个页面的处理,知道了构建多页面应用可以实现css代码的模块化。那么,构建多页面应用能实现html代码的模块化吗?当然可以。在上一篇文章中,有一个title不能注入到生成的页面的问题,但是html-webpack-plugin插件可以解析<%= htmlWebpackPlugin.options.title %>这样的语法,它内部可以写js语法,同样它也解决了title不能注入到生成的页面的问题,但它有一个限制条件就是只能使用在被当作模板的html文件中,其它的代码块无法使用,所以关于html代码块的模块化,我们可以在模板文件上下下功夫。使用过jQuery的同学都知道,我们可以使用$(’#container’).load(’./pages/partial.html’)的方法引入一个html代码块。而在webapck中,我们可以使用require(’./pages/commons/header.html’)的语法引入一个html代码块,但是webapck使用require引入的是一个文件流,不能和html模板文件相融合。所以这里要引入html-loader来做处理,它可以将流文件转化为字符串,这样就可以在html模板文件中使用了。基于html-webpack-plugin的模板的实际操作首先,本文中的构建多页面应用的项目目录图:├── src │ ├── common // 公用的模块 │ │ ├── a.js // 引用了a.css,模块header.css,container.css, footer.css │ │ ├── b.js // 引用了b.css │ │ ├── c.js // 引用了c.css │ │ ├── d.js ├── pages // html代码块 │ ├── template.html // 模板文件 │ ├── commons │ │ ├── header.html │ │ ├── footer.html │ │ ├── container.html ├── assets // 静态资源 │ ├── 19224132.jpg // 用来做页面图标 │ ├── css │ │ ├── a.css │ │ ├── b.css │ │ ├── c.css ├── assets // 静态资源 │ ├── 19224132.jpg // 用来做页面图标 │ ├── css │ │ ├── a.css │ │ ├── b.css │ │ ├── c.css │ │ ├── main.css │ │ ├── abutus.css │ │ ├── footer.css │ │ ├── container.css │ │ ├── header.css │ ├── uttils // 工具 │ │ ├── load.js // 工具代码load.js │ ├── index.js // 主模块index.js (包含a.js, b.js, c.js, d.js),引用了main.css │ ├── aboutUs.js // 主模块aboutus.js (包含a.js, b.js),引用了main.css, aboutus.css │ ├── contactUs.js // 主模块contactus.js (包含a.js, c.js),引用了main.css ├── webpack.config.js // css js 和图片资源 ├── package.json ├── yarn.lock新增了pages目录,它里面包含了html-webpack-plugin所需的模板文件和组成模板的各个模块文件。根据上文的分析,以及讲述需要,我们定义了一个公用的模板文件template.html,代码如下:<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”> <meta http-equiv=“X-UA-Compatible” content=“ie=edge”> <title><%= htmlWebpackPlugin.options.title %></title></head><body> <!– header –> <!– <%= require(‘html-loader!./commons/header.html’) %> –> <%= require(’./commons/header.html’) %> <!– container –> <!– <%= require(‘html-loader!./commons/container.html’) %> –> <%= require(’./commons/container.html’) %> <!– footer –> <!– <%= require(‘html-loader!./commons/footer.html’) %> –> <%= require(’./commons/footer.html’) %></body></html>注:这里可能有的人会有个疑惑。因为熟悉构建单页面应用的人都将html-loader放到webapck.config.js文件中,这样可以一次配置,终身受用,如果你也像构建单页面应用那样操作的话,你需要小心再小心,因为你不加控制的话,html-loader 也会处理template.html,再加之<%= htmlWebpackPlugin.options.title %>是html-webpack-plugin插件的独有语法,这样在webapck编译的过程中,因为html-loader先执行,所以会将它转化为字符串,而html-webpack-plugin后执行,所以,这样的情况下,webpack生成的html页面不是你所需要的。解决方法也很简单你,只需要使用include选项即可(在webapck中使用include和exclude可以是loader的处理更精确,加开快编译的速度)。当然,根据世界开发需要,我们可根据页面的不同设计效果和组成页面的不同模块来定义不同的html模板。而对于哪些组成html模板的html模块,我们可以像写普通的html代码块那样,如:header.html,它的代码如下:<div class=“header”> <ul> <li><a href=“index.html”>首页</a></li> <li><a href=“aboutus.html”>关于我们</a></li> <li><a href=“contactus.html”>联系我们</a></li> </ul></div>如果你要对生成的html页面压缩,可以使用html-webpack-plugin的minify选项。这样,构建页面应用的js、css、html代码的模块化就都实现了。源代码可参考webpack4.x multi-page文章到这里,基本算是完成了这一篇文章的目的。但是如果你想对html代码进行更加细粒度的处理,可以考虑ejs或者pug。正如本文开始说的那样,下面就只简单的介绍pug的使用。用pug对html代码进行细粒度的操作这里,需要使用到的是pug-loader,它的作用和html-loader类似,只不过它有自己的语法,要经过从pug的语法到html的转换过程。但是它有更灵活的语法,可以让我们的页面代码更简洁。有些内容只用语言可能太空洞,还是用代码还解释。首先,模板的代码:doctype htmlhtml(lang=“en”) head meta(charset=“UTF-8”) meta(name=“viewport” content=“width=device-width, initial-scale=1.0”) meta(http-equiv=“X-UA-Compatible” content=“ie=edge”) title= htmlWebpackPlugin.options.title body // header include ./commons/header.pug // container include ./commons/container.pug // footer include ./commons/footer.pug注:因为这里使用了pug的语法,所以要使用pug依赖包,它实现的是pug语法到html的转换,而pug-loader只是起到了一个加载解析的作用,所以使用前,我们要执行如下的安装命令:yarn add -D pug pug-loader因为pug不仅可以使用包含,还可以使用集成,扩展,迭代和混合的特性,可以让我们随心所欲的对html实现模块化,所以深受开发者的喜爱。它们的具体使用可参考pug 官方文档。具体示例可参考webpack3.x multi-page的源代码。本篇文章要介绍的内容,到这里是真的结束了。当然,还有很多东西没有说完,如果需要可关注后续的文章。构建多页面应用系列文章webpack 构建多页面应用——初探构建多页面应用——单个页面的处理构建多页面应用——模板 ...

December 21, 2018 · 2 min · jiezi

构建多页面应用——单个页面的处理

在看这篇文章之前,需要你对构建多页面应用有一定的基础认识,如果没有的话,可以先参考这篇文章webpack 构建多页面应用。多页面应用是由一个个独立的页面组成。因此,细粒度的处理一个个单页面是构建单页面框架之后的一个重要实现。因为所涵盖的知识点较碎,所以就不按照页面的位置结合组成元素来讲,如:head, body, script等。这里主要介绍head。因为script操作其实就是上一篇文章中已经介绍过的js操作,而body因为内容较多,需要另起一篇文章。页面的头部在上一篇文章中,我们讲述了如何用html-webpack-plugin 生成一个html文件,其中使用了两个配置项chunks,filename,前者指代页面所要引入的js模块,也就是我们常见的html页面中的<script src="…"></script>形式,后者指代文件的名字。那么,在这一部分,要说的就是如何给不同的页面配置生成不同的页面<head>…</head>。我们都知道页面头部包括title、link/style、meta、script 这四部分组成,尤其前三者居多。当然,在web前端开发中js很强大,我们可以用js直接控制,在不同页面的入口js文件中写相应的js代码。这种方法虽然可行,但维护起来比较麻烦,当你修改的时候,你需要查找一个个页面。相对来讲,使用html-webpack-plugin提供的配置项,会使你的开发工作变得简单起来。html-webpack-plugin 插件的配置项title 选项可以为页面指定名字,meta 选项可以为页面指定html文档关联信息,如:描述,作者等,favicon 可以为页面添加一个小图标。 修改 webpack.config.js,代码如下:…nnew HtmlWebapckPlugin({ /* inital page / filename: ‘index.html’, chunks: [‘index’], / page head */ title: ‘index’, meta: { ‘description’: ‘这是首页’, ‘keywords’: ‘webpack, multi-page, 首页’, ‘author’: ‘https://github.com/lvzhenbang/ }, favicon: ‘./assets/19884132.jpg’})…这样头部常用的三个元素我们已经解决了两个。那么接下来就是解决link这个元素的。注:有一个比较特殊的就是html页面图标<link rel=“shortcut icon” href=“19884132.jpg”> ,我们使用 html-webpack-plugin 插件的 favicon 选项已经解决。link 和 style 部分的处理这两个元素常常被用来处理样式。link 处理外部样式,style 处理内联样式。注:很多人会误解,或曲解,这里的样式处理是这样的:在定义的页面入口文件,或者页面入口文件引用的文件中,引入css文件,webapck会将这些样式以内联的形式或者link的形式注入到生成的html页面中。这样我们的应用的目录结构就变成如下这样(本片文章使用如下的目录结构,它也介绍了各个js文件对css文件的引用):├── src │ ├── common // 公用的模块 │ │ ├── a.js // 引用了a.css │ │ ├── b.js // 引用了b.css │ │ ├── c.js // 引用了c.css │ │ ├── d.js ├── assets // 静态资源 │ ├── 19224132.jpg // 用来做页面图标 │ ├── css │ │ ├── a.css │ │ ├── b.css │ │ ├── c.css │ │ ├── main.css │ │ ├── abutus.css │ ├── uttils // 工具 │ │ ├── load.js // 工具代码load.js │ ├── index.js // 主模块index.js (包含a.js, b.js, c.js, d.js),引用了main.css │ ├── aboutUs.js // 主模块aboutus.js (包含a.js, b.js),引用了main.css, aboutus.css │ ├── contactUs.js // 主模块contactus.js (包含a.js, c.js),引用了main.css ├── webpack.config.js // css js 和图片资源 ├── package.json ├── yarn.lock处理为内联样式如果是webpack3.x 推荐使用 css-loader,style-loader,extract-text-webpack-plugin;如果是webapck4.x推荐使用的 css-loader, mini-css-extract-plugin。webpack3.x与webapck4.x都一样,修改webpack.config.js如下:…module: { rules: [ { test: /.css$/, use: [ ‘style-loader’, ‘css-loader’ ] } ]},…因为mini-css-extract-plugin是专门为webpack4.x设计的,如果webapck3.x使用它会报错。处理为外部链接(link)webpack3.x中webpack.config.js修改如下:…const ExtractTextPlugin = require(’extract-text-webapck-plugin’)… module: { rules: [ { test: /.css$/, use: ExtractTextPlugin.extract({ fallback: ‘style-loader’, use: ‘css-loader’ }) } ] }, plugins: [ … new ExtractTextPlugin({ filename: ‘[name].css’ }) ]webpack4.x中webpack.config.js修改如下:…const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’)… module: { rules: [ { test: /.css$/, use: [ MiniCssExtractPlugin.loader, ‘css-loader’ ] } ] }, plugins: [ … MiniCssExtractPlugin() ],从js文件中分离出css文件,webpack3.x借助于extract-text-webpack-plugin,webpack4.x借助于mini-css-extract-plugin,前者给不同的css代码块命名需要在extract-text-webapck-plugin的示例中配置,它使用的是内置的CommonsChunkPlugin插件的拆分原则,后者不需要配置分离css代码块的名字选项,后者借助于SplitsChunkPlugin插件的拆分原则。所以,它们的分离形式与js代码块一致。webpack3.x为每个入口点生成了一个css文件,并提取了它们的公共代码生成了一个新的css文件;webapck4.x为每个入口生成了一个css文件,并提取并生成了这些文件相互之间的公共文件(它和前者不同,后者更精细化,只要是某一个或者几个文件有公共代码就提取出来,然后生成新的文件)。为什么将css文件和js文件分的这么细?是因为这样可以显著的减小首次加载页面时请求文件的大小(lazyload),但是这样做会增加HTTP的请求次数。在多页面应用的过程中,有的人喜欢将所有的css放在一个或两个文件中,而不是像本文中那样为每个页面生成一个css文件,包括它们之间的共用文件。但在多页面应用中,这样精密的细分也有其好处。相对来说,使用CommonsChunkPlugin拆分的css模块更合理些,而使用SplitsChunkPlugin拆分的css模块,则过于细化。至于如何取舍,还需要根据实际情况来定。当然,这里面还有一些小的问题需要优化,后期我会视情况来写相应的文章描述。源代码webpack3.x multi-pagewebpack4.x multi-page构建多页面应用系列文章webpack 构建多页面应用——初探构建多页面应用——单个页面的处理 ...

December 18, 2018 · 2 min · jiezi

webpack 构建多页面应用

如何使用webpack构建多页面应用,这是一个我一直在想和解决的问题。网上也给出了很多的例子,很多想法。猛一看,觉得有那么点儿意思,但仔细看也就那样。使用webpack这个构建工具,可以使我们少考虑很多的问题。我们常见的单页面应用只有一个页面,它考虑问题,解决问题围绕着中心化去解决,因此很多麻烦都迎刃而解。如果你使用过vue.js,那么想必你一定用过vue-router,vuex,它们就是典型的中心化管理模式,当然还有很多,这里不一一列举了。而多页面应用,我们不能再按照中心化模式的路走了,因为行不通,这也是很多人认为多页面应用不好做,或者干脆认为webapck只能做单页面应用,而不能做多页面应用的原因。所以,我要说明的第一点儿是:不要用做单页面应用的思维来做多页面应用。单页面中的模块共享和多页面的模块共享的区别单页面的模块共享,其实是代码块在同一个页面的不同位置的重复出现;而多页面应用的代码块儿共享需要实现的不仅是同一个页面的共享,还要做到跨页面的共享。所以,第一个要解决的问题是:不同页面的代码块共享如何实现?单页面的路由管理,其实是根据用户的触发条件来实现不同的代码块的显隐;而多页面应用的路由管理则不然,它实现的是页面的跳转。所以,第二个要解决的问题是:所页面应用的导航该如何做?单页面的状态管理,很受开发者喜好。单页面是一个页面,所以页面中的数据状态的管理操作起来还算得心应手,那么多页面应用的呢,显然依靠它自身很难实现。所以,第三个要解决的问题是:多页面应用的状态管理如何做?注:这个问题问的其实有点儿傻,如果你做的是dom操作的多页面儿应用,就不用做状态管理了。如果你还是使用想vue.js这样的库,你就需要考虑要不要再用做多页面的状态管理了,因为此法儿就是为单页面应用做的,多页面儿行不通。多页面应用的探索入口(entry):webpack对入口不仅可以定义单个文件,也可以定义多个文件。熟悉当页面应用开发的对于下面的代码应该不会陌生吧?module.exports = { entry: ‘./src/index.js’, ···}我第一次接触真正的单页面应用项目使用的就是angualrjs,使用的构建工具使webapck+gulp,其中的webpack.config.js 中的看到的入口文件代码就是它。后来,接触到的是数组形式,代码如下:module.exports = { entry: [’./src/index.js’, ‘bootstrap’] ···}这样,将bootstrap和入口文件一起引用,就可以在任何一个代码块中使用boostrap。再后来,接触到的是对象形式,代码如下:module.exports = { main: ‘./src/index.js’ ···}这样做的目的是为了给输出的文件指定特定的名字。再后来,就是做多页面应用,就需要用到如下的代码:module.exports = { entry: { index: ‘./src/index.js’, aboutUs: ‘./src/aboutus.js’, contactUs: ‘./src/contactus.js’ }}为了引入第三方库,我们可以像如下这样做:module.exports = { entry: { index: [’./src/index.js’, ’loadsh’], aboutUs: ‘./src/aboutus.js’, contactUs: [’./src/contactus.js’, ’lodash’] }}webpack3.x的探索但为了共享模块代码,我们需要像下面这这样做:const CommonsChunkPlugin = require(‘webpack’).optimization.CommonsChunkPluginmodule.exports = { entry: { index: [’./src/index.js’, ‘./src/utils/load.js’, ’loadsh’], aboutUs: [’./src/aboutus.js’, ’loadsh’], contactUs: [’./src/contactus.js’,’./src/utils/load.js’, ’lodash’] }, plugins: [ new CommonsChunkPlugin({ name: “commons”, filename: “commons.js”, chunks: [“index”, “aboutUs”, “contactUs”] }) ]}这样型就会形成如下所示的项目目录结构:├── src │ ├── common // 公用的模块 │ │ ├── a.js │ │ ├── b.js │ │ ├── c.js │ │ ├── d.js │ ├── uttils // 工具 │ │ ├── load.js // 工具代码load.js │ ├── index.js // 主模块index.js (包含a.js, b.js, c.js, d.js) │ ├── aboutUs.js // 主模块aboutus.js (包含a.js, b.js) │ ├── contactUs.js // 主模块contactus.js (包含a.js, c.js) ├── webpack.config.js // css js 和图片资源 ├── package.json ├── yarn.lock但是这个内置插件的局限性比较大。正如上面所使用的那样,它只会提取chunks选项所匹配的模块共有的代码块。就如同上面代码表示的那样,它只会提取pindex, aboutUs, contactUs共有的代码块loadsh,而不会提取index, contactUs共有的代码块load.js。当然,一般的第三方库,我们也不这样使用,而是像下面这样使用:const CommonsChunkPlugin = require(‘webpack’).optimization.CommonsChunkPluginmodule.exports = { entry: { index: [’./src/index.js’, ‘./src/utils/load.js’], aboutUs: [’./src/aboutus.js’], contactUs: [’./src/contactus.js’,’./src/utils/load.js’], vendors: [’lodash’] }, externals: { commonjs: “lodash”, root: “_” }, plugins: [ new CommonsChunkPlugin({ name: “commons”, filename: “commons.js”, chunks: [“index”, “aboutUs”, “contactUs”] }) ]}对于web应用最终的目的是:匹配生成不同的html页面。这里我们要使用的就是html-webpack-plugin。首先,需要安装html-webpack-plugin:yarn add –dev html-webpack-plugin然后引入插件,并配置如下:…const HtmlWebapckPlugin = require(‘html-webpack-plugin’);… plugins: [ … new HtmlWebapckPlugin({ filename: ‘index.html’, chunks: [‘vendors’, ‘commons’, ‘index’] }), new HtmlWebapckPlugin({ filename: ‘aboutUs.html’, chunks: [‘vendors’, ‘commons’, ‘aboutUs’] }), new HtmlWebapckPlugin({ filename: ‘contactUs.html’, chunks: [‘commons’, ‘contactUs’] }) ], …这样一个基于webpack3.x的多页面框架就有了基本的样子。webpack4.x的探索而使用webpack4.x则完全不同,它移除了内置的CommonsChunkPlugin插件,引入了SplitChunksPlugin插件,这个插件满足了我们的需要,弥补了CommonsChunkPlugin的不足。如果你想要解决之前的不足,去提取index, contacUs共有的模块,操作起来会很简单。正如上面的所列举的那样,我们有三个入口点index, aboutUs, contactUs,SplitChunksPlugin 插件会首先获取这三个入口点共有的代码块,然后建立一个文件,紧接着获取每两个入口点的共有代码块,然后将每个入口点独有的代码块单独形成一个文件。如果你使用了第三方库,就像上面我们使用的loadsh,它会将第三方入口代码块单独打包为一个文件。配置文件webpack.config.js需要增加如下的代码:···optimization: { splitChunks: { chunks: ‘all’, maxInitialRequests: 20, maxAsyncRequests: 20, minSize: 40 }}···因为SplitChunksPlugin可以提取任意的入口点之间的共同代码,所以,我们就不需要使用vendors入口节点了。那么,为匹配生成不同的页面代码可以修改成如下:const HtmlWebapckPlugin = require(‘html-webpack-plugin’)··· plugins: [ new HtmlWebapckPlugin({ filename: ‘index.html’, chunks: [‘index’] }), new HtmlWebapckPlugin({ filename: ‘aboutUs.html’, chunks: [‘aboutUs’] }), new HtmlWebapckPlugin({ filename: ‘contactUs.html’, chunks: [‘contactUs’] }), ]···可以发现结果越来越接近我们所想。但是这里还是存在一个问题,第三方库loadsh因为在入口点index, aboutUs中被分别引入,但是构建的结果却输出了两个第三方库文件,这不是我们想要的。这个问题怎么解决呢,因为html-webpack-plugin插件的chunks选项,支持多入口节点,所以,我们可以再单独创建一个第三方库的入口节点vendors。配置代码修改如下:… entry: { index: [’./src/index.js’, ‘./src/utils/load.js’], aboutUs: [’./src/aboutUs.js’], contactUs: [’./src/contactUs.js’,’./src/utils/load.js’], vendors: [’loadsh’] }, … plugins: [ new HtmlWebapckPlugin({ filename: ‘index.html’, chunks: [‘index’, ‘vendors’] }), new HtmlWebapckPlugin({ filename: ‘aboutUs.html’, chunks: [‘aboutUs’, ‘vendors’] }), new HtmlWebapckPlugin({ filename: ‘contactUs.html’, chunks: [‘contactUs’] }), ],…注意:如果不同的入口点儿之间有依赖关系,如上面的index和vendors之间,因为index依赖于vendors,所以vendors要置于index之前。这篇文章,说到这里基本上已经结束了。当然,webpack多页面应用的知识点还没有讲完,这些内容会放在后续的文章中详解。源代码webpack3.x multi-pagewebpack4.x multi-page ...

December 17, 2018 · 2 min · jiezi

webpack4 系列教程(十四):Clean Plugin and Watch Mode

作者按:因为教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步《webpack4 系列教程(十四):Clean Plugin and Watch Mode》原文地址。更欢迎来我的小站看更多原创内容:godbmw.com,进行“姿势”交流 ♪(^∇^*)0. 课程介绍和资料>>>本节课源码>>>所有课程源码本节课的代码目录如下:本节课用的 plugin 和 loader 的配置文件package.json如下:{ “devDependencies”: { “clean-webpack-plugin”: “^0.1.19”, “html-webpack-plugin”: “^3.2.0”, “webpack”: “^4.16.1” }}1. 什么是Clean Plugin和Watch Mode?在实际开发中,由于需求变化,会经常改动代码,然后用 webpack 进行打包发布。由于改动过多,我们/dist/目录中会有很多版本的代码堆积在一起,乱七八糟。为了让打包目录更简洁,这时候需要Clean Plugin,在每次打包前,自动清理/dist/目录下的文件。除此之外,借助 webpack 命令本身的命令参数,可以开启Watch Mode:监察你的所有文件,任一文件有所变动,它就会立刻重新自动打包。2. 编写入口文件和 js 脚本入口文件app.js代码:console.log(“This is entry js”);// ES6import sum from “./vendor/sum”;console.log(“sum(1, 2) = “, sum(1, 2));// CommonJsvar minus = require(”./vendor/minus”);console.log(“minus(1, 2) = “, minus(1, 2));// AMDrequire([”./vendor/multi”], function(multi) { console.log(“multi(1, 2) = “, multi(1, 2));});vendor/sum.js:export default function(a, b) { return a + b;}vendor/multi.js:define(function(require, factory) { “use strict”; return function(a, b) { return a * b; };});vendor/minus.js:module.exports = function(a, b) { return a - b;};3. 编写 webpack 配置文件CleanWebpackPlugin参数传入数组,其中每个元素是每次需要清空的文件目录。需要注意的是:应该把CleanWebpackPlugin放在plugin配置项的最后一个,因为 webpack 配置是倒序的(最后配置的最先执行)。以保证每次正式打包前,先清空原来遗留的打包文件。const webpack = require(“webpack”);const HtmlWebpackPlugin = require(“html-webpack-plugin”);const CleanWebpackPlugin = require(“clean-webpack-plugin”);const path = require(“path”);module.exports = { entry: { app: “./app.js” }, output: { publicPath: __dirname + “/dist/”, // js引用路径或者CDN地址 path: path.resolve(__dirname, “dist”), // 打包文件的输出目录 filename: “[name]-[hash:5].bundle.js”, chunkFilename: “[name]-[hash:5].chunk.js” }, plugins: [ new HtmlWebpackPlugin({ filename: “index.html”, template: “./index.html”, chunks: [“app”] }), new CleanWebpackPlugin([“dist”]) ]};执行webpack打包,在控制台会首先输出一段关于相关文件夹已经清空的的提示,如下图所示:4. 开启Watch Mode直接在webpack命令后加上–watch参数即可:webpack –watch。控制台会提示用户“开启 watch”。我改动了一次文件,改动被 webpack 侦听到,就会自动重新打包。如下图所示:如果想看到详细的打包过程,可以使用:webpack -w –progress –display-reasons –color。控制台就会以花花绿绿的形式展示出打包过程,看起来比较酷炫: ...

October 22, 2018 · 1 min · jiezi

webpack4带来了什么

在开发人员还在体会webpack3.x的余韵时,webpack4.x已经悄然而来。而对使用者来说,最期待的问题无外乎如下:新版本与旧版本相比都有哪些改变?webpack3.x到webapck4.x的迁移?使用webpack4.x我们应该注意什么?webpack的新特性webpack 作为构建工具的强大之处在于:可以在 webpack.config.js 中配置很多独特的功能;它的配置灵活多变;但正因为这样,这也是它的糟点。因为太随意,所以不好控制,造成了如下的问题:学习、使用、研究webpack的成本过高(进阶曲线太陡);构建一个小应用也需要像构建大应用那样配置 webpack.config.js(麻雀虽小五脏俱全);而webpack4.x作为新一代版本 webpack ,它的出现极大的解决了现有的问题。webpackk4.x可以不使用 webpack.config.js 配置文件可以使用下面6小步完成项目的构建:创建一个项目目录(webpack-demo),然后进入改目录mkdir webpack-demo && cd webpack-demo初始化 package.json 文件npm init -y加载 webpack 和 webpack-cli 依赖npm install webpack webpack-cli –save-dev在项目中添加 /src/index.js 文件(index.js 是默认的入口文件,默认入口目录为/src,当然你也可以自定义入口文件,需要修改 package.json 中的 main 配置项为指定的文件)index.js 文件代码如下:console.log(‘hello webpack.’)打开 package.json 在 scripts 配置项中添加如下代码:“scripts”: { “build”: “webpack”}注:这就是NPM的 scripts 命令运行 npm run build 命令,之后在项目中你将看到一个 ~/dist/main.js 的文件。在命令窗口你因该注意到如下的警告提示:WARNING in configurationThe ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.You can also set it to ’none’ to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/忽略这条提示信息,我们发现webpack4.x的项目初始化配置和webpack3.x没什么大的区别,但是webpack4.x少了必须要的 webpack.config.js 配置文件。打包模式的改变我们再回头查看上面这个提示信息,它的意思就是说:‘如果没有设置打包模式这个配置项,那么默认的打包模式为生产模式(production),而对于开发模式(development),需要配置 mode 配置项’,说到这里,我想各位看官应该明白了webpack4.x增加了很多默认配置项,针对不了解webpack的人员或小应用开发的场景,这样做无异省时省力。但实际应用中,我们往往还是区分开发模式和生产模式,但这在webpack4.x中也不是什么难事儿,只要修改 package.json 中的 scripts 如下:“scripts”: { “dev”: “webpack –mode development”, // 用于开发模式 “build”: “webpack –mode production” // 用于生产模式}‘对!webpack4.x就是这么简单’。我们不需要像webpack3.x那样分别定义开发模式和生产模式这样两份配置文件。重载默认的配置项入口/出口没有了配置文件 webpack.config.js ,在减少了我们的配置工作量同时,也给初窥门径的我们带来了一些疑问。例如:如何自定义入口/出口?在没有 webpack.config.js 的情况下,我们可以在命令行中添加入口/出口配置项,代码如下:“scripts”: { “dev”: “webpack –mode development ./src/entry.js –output ./dist/bundle.js”, // 用于开发模式 “build”: “webpack –mode production ./src/entry.js –output ./dist/bundle.min.js” // 用于生产模式}这只是不使用 webpack.config.js 的一种方案。以上就是webpack4.x给我们带来的整体变化。但是原来 webpack.config.js 配置文件中的 module 和 plugins 配置项中的功能实现还是需要使用 webpack.config.js。虽然webpack团队的计划是 0 配置一些常用的loader,plugin,但实现的仅有 UglifyJSPlugin 内置插件,在生产模式无需引入它就可以实现 *.js 代码的压缩。其它的loader和plugin则只能通过 webpack.config.js 来引入。webpack的迁移和注意事项看到webpack4.x的这些变化,很多人不仅会问webpack3.x到webpack4.x的迁移是不是很麻烦,其实并不麻烦,webpack4.x向后兼容webpack.3x。前面为了不引入 webpack.config.js ,我们使用了npm的 scripts ,其时像入口/出口的重载,我们也可以在 webpack.config.js 配置文件中完成,配置跟原来的相似,但是webpack4.x有如下问题需要注意:升级到webpack4.x,你会发现在使用 extract-text-webpack-plugin 分离 *.css 出文件时经常出错,这是 extract-text-webpack-plugin 本身的问题,官方推荐使用 mini-css-extract-plugin 来避免问题的出现,但使用 mini-css-extract-plugin 有一个限制就是webapck须是4.2.0版本以上(较低的版本不支持)。使用 使用babel-loader 转化ES6->ES5时将不需要 .babelrc 配置文件,你只需要在 package.json 的 scripts 中添加 –module-bind js=babel-loader 即可完成对 babel-loader 的配置。其他的loader和plugin没有什么大的变化。其实讲到这里基本完了,下面是用webpack4.x构建的一个demo。webpack4.x的demo紧接上面的配置:首先,添加 html-wepback-plugin 和 html-loader 依赖:npm install html-webpack-plugin html-loader –save-devhtml-webpack-plugin 生成html文件(html文件用来加载打包生成 bundle.js 文件),当然你也可以使用webpack支持的各种模板loader,这里使用 html-loader 支持的 *.html 类型模板来生成。其次,添加 mini-css-extract-plugin 和 css-loader 依赖:npm install mini-css-extract-plugin css-loader –save-devloader和plugin配置与webpack3.x类同,也可参考下面提供代码中的 webpack.config.js 文件。然后,添加 babel-loader 、@babel/babel-core 和 @babel/babel-preset 依赖:npm install @babel/core babel-loader @babel/preset-env –save-devloader和plugin配置与webpack3.x类同,也可参考下面提供源码中的 webpack.config.js 文件。修改 package.json 中 scripts 如下:“scripts”: { “dev”: “webpack –mode development –module-bind js=babel-loader ./src/entry.js –output ./dist/bundle.js”, “build”: “webpack –mode production ./src/entry.js –module-bind js=babel-loader –output ./dist/bundle.min.js”},最后,添加 webpack-dev-server 依赖,实现项目文件修改,浏览器及时刷新npm install webpack-dev-server在 package.json 中 scripts 的 dev 替换 webpack 为 webpack-dev-server 即可,代码如下:“scripts”: { “dev”: “webpack-dev-server –mode development –module-bind js=babel-loader ./src/entry.js –output ./dist/bundle.js”, “build”: “webpack –mode production ./src/entry.js –module-bind js=babel-loader –output ./dist/bundle.min.js”},这样一个简单的demo就完成了。其他的loader和plugin配置和webpack3.x类同。wepback4-demo源代码参考资料webpack-tutorialwebpack官方指南我的webpack系列文章 ...

October 18, 2018 · 2 min · jiezi