关于webpack:深入理解webpack自动刷新浏览器

咱们在日常开发时,有一个须要在开发状态下的优化,就是浏览器能主动显示批改后的代码,而无需咱们手动刷新 1. 主动刷新浏览器为了能实现浏览器主动刷新,须要做两件事件: 监听文件变动主动刷新浏览器1.1 监听文件变动监听文件变动是在webpack模块进行。 1.1.1 形式须要在webpack中开启监听模式,有两种形式(开启监听模式后,能够设置监听相干配置watchOptions): 1. 在webpack配置文件中增加watch:true module.export = { watch: true, watchOptions: { // 不监听的文件或文件夹 ignored: /node_modules/, // 监听到变动产生后会等300ms再去执行动作,避免文件更新太快导致从新编译频率太高 aggregateTimeout: 300, // 判断文件是否发生变化是通过不停的去询问零碎指定文件有没有变动实现的 poll: 1000 }}2. 在执行启动 Webpack 命令时,带上 --watch 参数 1.1.2 原理在 Webpack 中监听一个文件发生变化的原理是定时(可在watchOptions.poll中设置)的去获取这个文件的最初编辑工夫,每次都存下最新的最初编辑工夫,如果发现以后获取的和最初一次保留的最初编辑工夫不统一,就认为该文件产生了变动当发现某个文件产生了变动时,并不会立即通知监听者,而是先缓存起来,收集一段时间(可在watchOptions.aggregateTimeout中设置)的变动后,再一次性通知监听者。避免在编辑代码的过程中可能会高频的输出文字导致文件变动的事件高频的产生1.2 主动刷新浏览器监听到文件变动后须要去刷新浏览器,这部分在webpack-dev-server模块中进行。(在应用 webpack-dev-server 模块去启动 webpack 模块时,webpack 模块的监听模式默认会被开启) 1.2.1 原理主动刷新有三种办法: 借助浏览器扩大去通过浏览器提供的接口刷新往要开发的网页中注入代理客户端代码,通过代理客户端去刷新整个页面。把要开发的网页装进一个 iframe 中,通过刷新 iframe 去看到最新成果。DevServer 反对第2、3种办法,第2种是 DevServer 默认采纳的刷新办法。 2. 模块热更新以上主动刷新是会刷新整个页面,这种形式的毛病就是工夫长,同时不能保留页面的状态。 而模块热更新即可在不刷新整个页面的状况下来实时预览。它只会在代码发生变化时,只编译发生变化的模块,并替换浏览器中的老模块。 2.1 两种形式以下两种形式都能实现模块热替换,区别在于其外部应用的通信形式不同。前者应用webcoket通信,后者应用eventSource通信。 2.1.1 webpack-dev-server1. 开启形式 开启DevServer的模块热替换模式: webpack-dev-server --hot增加HotModuleReplacementPlugin插件2. 原理 ...

December 6, 2020 · 1 min · jiezi

关于webpack:你可能不知道的9条Webpack优化策略

引言webpack的打包优化始终是个陈词滥调的话题,惯例的无非就分块、拆包、压缩等。 本文以我本人的教训向大家分享如何通过一些剖析工具、插件以及webpack新版本中的一些新个性来显著晋升webpack的打包速度和改善包体积,学会剖析打包的瓶颈以及问题所在。 本文演示代码,仓库地址 速度剖析 ????webpack 有时候打包很慢,而咱们在我的项目中可能用了很多的 plugin 和 loader,想晓得到底是哪个环节慢,上面这个插件能够计算 plugin 和 loader 的耗时。 yarn add -D speed-measure-webpack-plugin配置也很简略,把 webpack 配置对象包裹起来即可: const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");const smp = new SpeedMeasurePlugin();const webpackConfig = smp.wrap({ plugins: [ new MyPlugin(), new MyOtherPlugin() ]});来看下在我的项目中引入speed-measure-webpack-plugin后的打包状况:从上图能够看出这个插件次要做了两件事件: 计算整个打包总耗时剖析每个插件和 loader 的耗时状况晓得了具体loader和plugin的耗时状况,咱们就能够“隔靴搔痒”了体积剖析 ????打包后的体积优化是一个能够着重优化的点,比方引入的一些第三方组件库过大,这时就要思考是否须要寻找替代品了。 这里采纳的是webpack-bundle-analyzer,也是我平时工作中用的最多的一款插件了。 它能够用交互式可缩放树形图显示webpack输入文件的大小。用起来十分的不便。 首先装置插件: yarn add -D webpack-bundle-analyzer装置完在webpack.config.js中简略的配置一下: const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;module.exports = { plugins: [ new BundleAnalyzerPlugin({ // 能够是`server`,`static`或`disabled`。 // 在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。 // 在“动态”模式下,会生成带有报告的单个HTML文件。 // 在`disabled`模式下,你能够应用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。 analyzerMode: "server", // 将在“服务器”模式下应用的主机启动HTTP服务器。 analyzerHost: "127.0.0.1", // 将在“服务器”模式下应用的端口启动HTTP服务器。 analyzerPort: 8866, // 门路捆绑,将在`static`模式下生成的报告文件。 // 绝对于捆绑输入目录。 reportFilename: "report.html", // 模块大小默认显示在报告中。 // 应该是`stat`,`parsed`或者`gzip`中的一个。 // 无关更多信息,请参见“定义”一节。 defaultSizes: "parsed", // 在默认浏览器中主动关上报告 openAnalyzer: true, // 如果为true,则Webpack Stats JSON文件将在bundle输入目录中生成 generateStatsFile: false, // 如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。 // 绝对于捆绑输入目录。 statsFilename: "stats.json", // stats.toJson()办法的选项。 // 例如,您能够应用`source:false`选项排除统计文件中模块的起源。 // 在这里查看更多选项:https: //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21 statsOptions: null, logLevel: "info" ) ]}而后在命令行工具中输出npm run dev,它默认会起一个端口号为 8888 的本地服务器:图中的每一块清晰的展现了组件、第三方库的代码体积。 ...

December 4, 2020 · 5 min · jiezi

关于webpack:webpack输出文件分析

打包原理简略讲就是生成ast语法树,依据语法树生成对应的js代码这里仅剖析打包输入的后果,从后果剖析webpack对咱们代码做了啥剖析入口文件// main.js// 通过CommonJS标准导入const show = require('./show.js');// 执行 show 函数show('Webpack');依赖文件// show.js// 操作 DOM 元素,把 content 显示到网页上function show(content) { window.document.getElementById('app').innerText = 'Hello,' + content;}// 通过 CommonJS 标准导出 show 函数module.exports = show;打包后果// bundle.js( // webpackBootstrap 启动函数 // modules 即为寄存所有模块的数组,数组中的每一个元素都是一个函数 function (modules) { // 装置过的模块都寄存在这外面 // 作用是把曾经加载过的模块缓存在内存中,晋升性能 var installedModules = {}; // 去数组中加载一个模块,moduleId 为要加载模块在数组中的 index // 作用和 Node.js 中 require 语句类似 function __webpack_require__(moduleId) { // 如果须要加载的模块曾经被加载过,就间接从内存缓存中返回 if (installedModules[moduleId]) { return installedModules[moduleId].exports; } // 如果缓存中不存在须要加载的模块,就新建一个模块,并把它存在缓存中 var module = installedModules[moduleId] = { // 模块在数组中的 index i: moduleId, // 该模块是否曾经加载结束 l: false, // 该模块的导出值 exports: {} }; // 从 modules 中获取 index 为 moduleId 的模块对应的函数 // 再调用这个函数,同时把函数须要的参数传入 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // 把这个模块标记为已加载 module.l = true; // 返回这个模块的导出值 return module.exports; } // Webpack 配置中的 publicPath,用于加载被宰割进来的异步代码 __webpack_require__.p = ""; // 应用 __webpack_require__ 去加载 index 为 0 的模块,并且返回该模块导出的内容 // index 为 0 的模块就是 main.js 对应的文件,也就是执行入口模块 // __webpack_require__.s 的含意是启动模块对应的 index return __webpack_require__(__webpack_require__.s = 0); })( // 所有的模块都寄存在了一个数组里,依据每个模块在数组的 index 来辨别和定位模块 [ /* 0 */ (function (module, exports, __webpack_require__) { // 通过 __webpack_require__ 标准导入 show 函数,show.js 对应的模块 index 为 1 const show = __webpack_require__(1); // 执行 show 函数 show('Webpack'); }), /* 1 */ (function (module, exports) { function show(content) { window.document.getElementById('app').innerText = 'Hello,' + content; } // 通过 CommonJS 标准导出 show 函数 module.exports = show; }) ]);// 以上看上去简单的代码其实是一个立刻执行函数,能够简写为如下:(function(modules) { // 模仿 require 语句 function __webpack_require__() { } // 执行寄存所有模块数组中的第0个模块 __webpack_require__(0);})([/*寄存所有模块的数组*/])能够看到bundle.js是一个自执行函数,入参就是main.js和show.js革新后的代码块所形成的数组自执行函数里运行了__webpack_require__这个函数,入参是0,0其实就是代码块数组中对应的入参,示意第一个代码块再来看__webpack_require__函数,首先执行的是缓存判断,通过moduleId判断之前是否曾经加载过,如果加载过,间接返回间接的加载后果exports,mouduleId就是不同代码模块在入参数组中的index而如果没有加载过,则新建一个对象,重要的是这个对象中的exports属性,外面寄存的就是加载模块后,对应模块export进去的货色而后用这个exports作为上下文去执行对应的代码块,传递参数为方才新建的module,module里的exports,以及__webpack_require__这个办法自身而后看到main.js中的require被革新成了__webpack_require__,__webpack_require__(1)代表加载第二个代码块第二个代码块中,定义了show这个办法,而后show会作为module.exports的导出,也就是赋值给了installedModules[0].module.exports,也就是这个导出曾经被缓存起来了,下次再有别的中央用到,会间接被导出这就是webpack大抵的打包思路,将各个独自的模块革新成数组作为入参,传给自执行函数,同时保护一个installedModules记录加载过的模块,利用模块数组中的index作为key值,exports记录导出对象按需加载因为单页利用也会有路由这个概念,在没有切换到对应路由之前,可能并不心愿浏览器对这部分页面的js进行下载,从而晋升首页关上的速度,就波及到一个懒加载,即按需加载的问题webpack的按需加载是通过import(XXX)实现的,import()是一个提案,而webpack反对了它// 异步加载 show.jsimport(/* webpackChunkName: 'show' */ './show').then((module) => { // 执行 show 函数 const show = module.default; show('Webpack');});通过这种形式打包,咱们能够发现最终打包进去的文件分成了两个,bundle.js和show.xxx.js其中/* webpackChunkName: 'show' */是专门正文给webpack看的,为的是指定按需加载的包的名字,同时记得在webpack的配置文件的entry中,配置chunkFilename: '[name].[hash].js',不然这个指定不会失效先来看入口文件,将临时没有用到的函数都暗藏后如下:(function (modules) { // webpackJsonp 用于从异步加载的文件中装置模块 window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) { // ... 先省略 }; // 缓存曾经装置的模块 var installedModules = {}; // 存储每个 Chunk 的加载状态; // 键为 Chunk 的 ID,值为0代表曾经加载胜利 var installedChunks = { 1: 0 }; // 模仿 require 语句,和下面介绍的统一 function __webpack_require__(moduleId) { // ... 省略和下面一样的内容 } // 用于加载被宰割进来的,须要异步加载的 Chunk 对应的文件 __webpack_require__.e = function requireEnsure(chunkId) { // ... 先省略 }; // 加载并执行入口模块,和下面介绍的统一 return __webpack_require__(__webpack_require__.s = 0);})( // 寄存所有没有通过异步加载的,随着执行入口文件加载的模块 [ // main.js 对应的模块 (function (module, exports, __webpack_require__) { // 通过 __webpack_require__.e 去异步加载 show.js 对应的 Chunk __webpack_require__.e('show').then(__webpack_require__.bind(null, 'show')).then((show) => { // 执行 show 函数 show('Webpack'); }); }) ]);能够看到import(xxx).then被替换成了__webpack_require__.e(0).then,__webpack_require__.e(0)返回了一个promise第一个then里相当于执行了__webpack_require__(1),但很显著能够看到自执行函数的入参数组只有一个元素,不存在[1],这个[1]是什么时候被插入的呢看一下__webpack_require__.e的实现__webpack_require__.e = function requireEnsure(chunkId) { // 从下面定义的 installedChunks 中获取 chunkId 对应的 Chunk 的加载状态 var installedChunkData = installedChunks[chunkId]; // 如果加载状态为0示意该 Chunk 曾经加载胜利了,间接返回 resolve Promise if (installedChunkData === 0) { return new Promise(function (resolve) { resolve(); }); } // installedChunkData 不为空且不为0示意该 Chunk 正在网络加载中 if (installedChunkData) { // 返回寄存在 installedChunkData 数组中的 Promise 对象 return installedChunkData[2]; } // installedChunkData 为空,示意该 Chunk 还没有加载过,去加载该 Chunk 对应的文件 var promise = new Promise(function (resolve, reject) { installedChunkData = installedChunks[chunkId] = [resolve, reject]; }); installedChunkData[2] = promise; // 通过 DOM 操作,往 HTML head 中插入一个 script 标签去异步加载 Chunk 对应的 JavaScript 文件 var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); script.type = 'text/javascript'; script.charset = 'utf-8'; script.async = true; script.timeout = 120000; // 文件的门路为配置的 publicPath、chunkId 拼接而成 script.src = __webpack_require__.p + "" + chunkId + ".bundle.js"; // 设置异步加载的最长超时工夫 var timeout = setTimeout(onScriptComplete, 120000); script.onerror = script.onload = onScriptComplete; // 在 script 加载和执行实现时回调 function onScriptComplete() { // 避免内存泄露 script.onerror = script.onload = null; clearTimeout(timeout); // 去查看 chunkId 对应的 Chunk 是否装置胜利,装置胜利时才会存在于 installedChunks 中 var chunk = installedChunks[chunkId]; if (chunk !== 0) { if (chunk) { chunk[1](new Error('Loading chunk ' + chunkId + ' failed.')); } installedChunks[chunkId] = undefined; } }; head.appendChild(script); return promise; };首先判断这个chunkId是否曾经加载过,如果是的话,间接返回一个resolve的promise如果不为空又不为0,阐明正在加载中,这里的installedChunks[chunkId]是一个数组,外面保留着[resovle, reject],是在发动网络申请的时候赋值的如果下面两个判断都没击中,阐明是没有加载过,上面开始结构加载办法,次要是通过jsonp的模式首先新建一个promise,并对installedChunks[chunkId]赋值,把这个promise以及他的resolve和reject保留在外面,这也是下面为什么能够通过判断installedChunks[chunkId]不为空又不为0即正处于申请当中,间接返回数组第三个值,即新建的promise,让后续操作能够在这个promise上进行回调的注册而后前面的办法就是通过结构一个script标签,插入到head中,保障代码能马上被下载,同时定义代码执行结束时的回调,判断是曾经加载了代码,如果加载胜利革除监听等,如果加载失败,抛出异样最初返回这个promise,供内部注册回调而这里通过jsonp加载的代码就是打包分离出来的另一个文件show.xx.js,也就是异步加载的show.js相干的代码webpackJsonp( // 在其它文件中寄存着的模块的 ID ['show'], // 本文件所蕴含的模块 {// show.js 所对应的模块 show: (function (module, exports) { function show(content) { window.document.getElementById('app').innerText = 'Hello,' + content; } module.exports = show; }) });接着看webpackJsonp这个办法是怎么定义的window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) { // 把 moreModules 增加到 modules 对象中 // 把所有 chunkIds 对应的模块都标记成曾经加载胜利 var moduleId, chunkId, i = 0, resolves = [], result; for (; i < chunkIds.length; i++) { chunkId = chunkIds[i]; if (installedChunks[chunkId]) { resolves.push(installedChunks[chunkId][0]); } installedChunks[chunkId] = 0; } for (moduleId in moreModules) { if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { modules[moduleId] = moreModules[moduleId]; } } while (resolves.length) { resolves.shift()(); } };chunkIds代表本人这个文件的id名,因为动静加载的时候,是利用动静加载的文件名形成script标签进行下载的,这里传入这个id是为了触发后续promise的resolve以及标记模块以及被加载moreModules就是对应的代码模块汇合executeModules 就是加载实现后须要被执行模块的index首先遍历installedChunks,后面提到过installedChunks[chunkId]通过网络下载的时候,回赋予三个值,代表其对应的promise,这里取出第一个resolve,保存起来,同时将加载标记置为0,示意已加载而后遍历动静加载的模块,把代码块塞到modules数组里最初执行之前保留下来的resolve函数,触发__webpack_require__.e(0).then的执行这样动静加载的代码通过结构jsonp进行下载,并且将对应代码传到bundle.js的modules中进行保留,而后在then函数中通过__webpack_require__执行模块,缓存输入这里为了便于了解,有对代码做肯定调整,实在的输入状况,能够通过具体打包输入查看,这里仅形容具体打包思路

December 3, 2020 · 3 min · jiezi

关于webpack:webpack4升5

yarn add webpack@next -D一、改良长久化缓存更好的算法和默认值改良长期缓存更好的tree shaking特点webpack 5 会移除所有废除的个性。为了无障碍的推动,应保障在构建时再无任何的废除正告。mode 必须webpack4=>5node.js 10.13webpack-cli降级到最新plugin 和 loader 降级到最新的可用版本降级废除的配置项- optimization.hashedModuleIds: true ↦ optimization.moduleIds: 'hashed'- optimization.namedChunks: true ↦ optimization.chunkIds: 'named'- optimization.namedModules: true ↦ optimization.moduleIds: 'named'- NamedModulesPlugin ↦ optimization.moduleIds: 'named'- NamedChunksPlugin ↦ optimization.chunkIds: 'named'- HashedModulesPlugin ↦ optimization.moduleIds: 'hashed'- optimization.noEmitOnErrors: false ↦ optimization.emitOnErrors: true- optimization.occurrenceOrder: true ↦ optimization: { chunkIds: 'total-size', moduleIds: 'size' }- optimization.splitChunks.cacheGroups.vendors ↦ optimization.splitChunks.cacheGroups.defaultVendors- Compilation.entries ↦ Compilation.entryDependencies- serve ↦ serve 已被移除,举荐应用 DevServer兼容性node: { Buffer: false, process: false }

November 28, 2020 · 1 min · jiezi

关于webpack:webpack-学习笔记三默认配置

在 webpack 学习笔记:疾速入门的练习中,咱们除了在我的项目中装置了 webpack 和 webpack-cli 以外,没有对 webpack 做任何配置,然而 webpack 仍然帮咱们胜利的打包了我的项目中的 JS 文件。 这是为什么呢?它是怎么晓得要打包 src/index.js 和 src/a.js 这两个文件的呢? 1. webpack 的默认配置当咱们不对 webpack 做任何配置,间接运行打包命令对我的项目进行打包操作时,webpack 理论是采纳了本身的一些默认配置。 在疾速入门的练习中,咱们用到了 webpack 的三个默认配置: 打包入口文件打包进口文件打包模式打包入口文件配置打包入口文件,简略来说就是配置 webpack 要从哪一个文件开始进行打包。 一个前端我的项目中通常都会有很多的文件,webpack 在构建我的项目时须要晓得到底要从哪一个文件开始进行打包。 webpack 默认的打包入口文件就是 src/index.js,之所以还能将 src/a.js 一起打包,是因为 webpack 在打包前还会剖析入口文件中的所有依赖文件,将它们全副加载进来后一起进行打包解决。 咱们能够试着将我的项目中的 src 文件换成其余的名字,例如 public,而后再执行一次打包命令。 从截图中能够看到,打包过程中报错了。而谬误提醒就是说“没有在 webpack-demo 目录中找到 src 文件”,因为 webpack 还是在依照默认配置去找 src/index.js 入口文件。 打包进口文件打包进口文件,简略来说就是 webpack 将我的项目中的代码打包胜利后生成的文件。 webpack 默认的打包进口文件就是 dist/main.js。它会主动在我的项目根目录创立 dist 目录,而后将打包好的 main.js 文件放入其中。 打包模式打包模式,指的就是 webpack 在打包时是采纳“开发模式”还是“生产模式”进行打包。 ...

November 20, 2020 · 1 min · jiezi

关于webpack:webpack-学习笔记webpack-简介

webpack 是什么? 咱们常常会在各种文章中看到对于 webpack 的介绍: webpack 是一个前端资源构建工具,是一个动态模块打包器。然而看完之后咱们通常还是一脸懵逼,官网的形容对于很多 webpack 的初学者来说太过于业余,导致大家并不能真正的理解到 webpack 到底是干什么的。 所以在这篇文章,我尽量用最简略的例子和形容,来给他们解释分明,webpack 到底是什么。 前端资源咱们始终说 webpack 是一个前端资源构建工具,这里提到的“前端资源”,指的并不是什么前端材料、文档、博客等,而是在前端我的项目开发中所须要的 HTML、CSS、JS、图片等资源。前端资源构建,也是针对这些代码资源进行构建。 构建工具那构建又是什么意思呢?咱们代码写好之后,为什么还须要构建呢?咱们来看一下上面这段代码: header { background-color: #cccccc; h1 { color: #3c3c3c; }}这是一段用 less 语法写的款式代码,咱们在 .html 文件中引入并在浏览器中运行该代码:运行后会发现 less 写的款式并没有失效,这是因为浏览器不能解析 less 的语法。 同样的情理,咱们在webpack 学习笔记:疾速入门一章中练习的代码,也是因为浏览器不能解析 ES6 的模块化语法导致浏览器报错。 所以,当咱们在一个我的项目中,应用了这些浏览器不能辨认的语法去写代码时,咱们就须要借助一些工具来帮咱们把代码转换成浏览器可能辨认的语法。 例如:能将 less 转换为 css 的工具,将 ES6 转换为 ES5 的工具。如果还有其余语法的代码,可能还须要更多的工具。 这个时候,前端就提出了一个“构建工具”的概念,意思就是咱们找一个大的工具,将这些小工具的性能都蕴含进来,开发者只须要学习这个大工具的应用就能够了。 而这个大工具,就是“构建工具”,webpack,就是构建工具的一种。 动态模块打包器后面咱们弄清楚了 webpack 是一个前端资源构建工具,那什么又是动态模块打包器呢? 有学过 Vue、React 等前端框架的同学可能会常常看到相似上面这种的代码: // index.js// 引入 js 资源import $ from 'jquery';import './a.js';// 引入款式资源import './b.css';import './c.less';// 引入图片、字体等资源// ...下面代码在一个 index.js 文件中引入了所有的资源,这个 index.js 文件咱们也称之为入口文件。 ...

November 20, 2020 · 1 min · jiezi

关于webpack:webpack-学习笔记快速入门

所谓疾速入门,就是实践这种货色咱们都先不讲,间接进入正题,上手去应用 webpack!然而呢,也只能仅限于入门级的应用,等咱们通过根本应用对 webpack 有个初步印象后,咱们仍然还是须要去从实践从细节去残缺的学习 webpack。 1. 我的项目初始化在本地新建一个空文件夹来作为这次入门练习的我的项目根目录。 在命令行工具(终端)中进入该文件目录,执行以下命令对我的项目进行初始化: npm init -y 该命令会应用默认参数在我的项目根目录中创立出 package.json 文件。 2. 装置 webpack在我的项目根目录中执行以下命令装置 webpack 和 webpack-cli。 npm i --save-dev webpack webpack-cli 3. 创立我的项目代码在我的项目根目录中新建 src/index.js 和 src/a.js 两个文件,代码如下: // src/a.jsexport const name = "有猫饼";console.log('a.js', name);// src/index.jsimport { name } from './a.js';console.log(name); 4. 运行我的项目代码在我的项目根目录创立一个 index.html 文件,将 src/index.js 文件引入。 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title></head><body> <script src="./src/index.js"></script></body></html>在浏览器中运行 index.html 代码会发现如下报错:报错起因是因为咱们没方法在模块以外的文件中应用 import 的语法。 5. webpack 打包 JS 代码在我的项目根目录中执行以下命令,打包 JS 文件: ...

November 19, 2020 · 1 min · jiezi

关于webpack:前端工程化模块化开发下22

模块化开发后面说了gulp工作流这边当初来讲下模块化开发模块化开发是当下最重要的开发范式模块化只是思维。模块化的演变过程: 文件划分的形式(原始形式齐全靠约定)毛病:1.净化全局作用域2.命名抵触3.无奈治理模块依赖关系2.命名空间形式(放到一个对象,而后再对立应用)3.而后就是AMD和CMD标准(社区逐步提出的标准,历史本章不作为重点讲)而后当初最初就是模块化标准的最佳实际:node.js的CommonJS和eES Modules (官网推出)Es Modules根本个性: 主动采纳严格模式,疏忽"use strict"每个ESM模块都是独自的公有作用域ESM是通过CORS去申请内部js模块 (须要容许CORS资源申请)ESM的script标签会提早执行脚本(不会像失常script标签立刻执行)这里导入的是一个只读性援用,所以模块内部不可更改,多个中央导入,模块外部值变动都会影响导入中央。import 是一个 导入申明,在头部应用,不可呈现在条件中属于是动态阶段确定依赖,能够按需引入依赖项,动静执行,理论执行的时候去模块内取值。和commonjs相互导入反对 export {}是固定语法,不是导出对象 动静导入import()返回一个promsie CommonJS特点: 所有代码都运行在模块作用域,不会净化全局作用域。模块能够屡次加载,然而只会在第一次加载时运行一次,而后运行后果就被缓存了,当前再加载,就间接读取缓存后果。要想让模块再次运行,必须革除缓存。模块加载的程序,依照其在代码中呈现的程序。属于动静加载,一导入会把其余模块导出的货色,整体导入对module对象的形容:1.module.exports属性 module.exports属性示意以后模块对外输入的接口,其余文件加载该模块,实际上就是读取module.exports变量。2.exports变量node为每一个模块提供了一个exports变量(能够说是一个对象),指向 module.exports。这相当于每个模块中都有一句这样的命令 var exports = module.exports; 这样,在对外输入时,能够在这个变量上增加办法。例如  exports.add = function (r){return Math.PI r r};留神:不能把exports间接指向一个值,这样就相当于切断了 exports 和module.exports 的关系。例如 exports=function(x){console.log(x)};一个模块的对外接口,就是一个繁多的值,不能应用exports输入,必须应用 module.exports输入。module.exports=function(x){console.log(x);}; 用阮老师的话来说,这两个不好辨别,那就放弃 exports,只用 module.exports 就好(手动机智)在浏览器咱们是不适宜应用CommonJS标准的,动静读取时查看到这个require模块,而后发申请返回文件,这会程序会卡在那里,这对服务器端不是一个问题,因为所有的模块都寄存在本地硬盘,能够同步加载实现,等待时间就是硬盘的读取工夫。然而,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。 所以浏览器应用的是ESM,然而因为它是es6的模块,有局部浏览器还不反对

November 17, 2020 · 1 min · jiezi

关于webpack:webpack40

file-loader给图片或者文件一个可拜访的地址outputPath:打包的相对路径publicPath:拜访门路,例如图片的src地址,绝对于html页面而言 Reference:webpack配置文档——publicPath url-loader将图片生成base64编码打包进boundle.js文件中。 如果图片比拟小,则能够应用url-loader打包进js中,缩小一次http申请。如果图片较大,则将图片应用file-loader打包进独自的文件夹中,配置拜访门路拜访。(应用limit:2048来管制)将css进行模块化打包:modules:true应用: postcss-loader主动增加厂商前缀,另需postcss.config.js配置文件,其中装置autoprefixer插件,(babel) HtmlWebpackPlugin在打包完结后主动生成html文件并引入boundle.js文件。 clean-webpack-plugin在每次构建前清理 /dist 文件夹new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),:在watch模式下触发增量构建时不删除index.html文件。 Reference:webpack文档——治理输入 devtool(source-map)论断: 在开发环境举荐应用 devtool: 'cheap-module-eval-source-map', 提醒比拟全,且打包速度比拟快。线上环境举荐应用 devtool: 'cheap-module-source-map'。devtool: 'inline-source-map':将source-map文件通过[data]URL的模式写入打包好的js文件底部。 devtool: 'source-map':打包会生成source-map文件。devtool: 'cheap-inline-source-map': 应用cheap更节约性能,检索谬误只准确到行,不准确到列,且只关怀业务代码。devtool: 'cheap-module-inline-source-map':相比于cheap,增加了module就会将谬误检索准确到第三方模块和loader等。devtool: 'eval':性能最好。将错误代码应用eval()执行,与源文件间接进行映射。 举荐浏览:https://segmentfault.com/a/1190000008315937https://www.html5rocks.com/en/tutorials/developertools/sourcemaps/http://www.ruanyifeng.com/blog/2013/01/javascript_source_map.htmlhttps://www.youtube.com/watch?v=NkVes0UMe9Y 主动编译代码webpack 提供几种可选形式,帮忙你在代码发生变化后主动编译代码:1. webpack watch mode(webpack 察看模式)2. webpack-dev-server3. webpack-dev-middlewaredevServerdevServer打包的文件寄存在内存中而不是磁盘中以晋升打包速度,所以dist目录下没有文件。拜访文件时按门路拜访即可。 HMR热更新对于css而言,style-loader底层曾经配置过 module.hot.accept 配置,所以无需再代码中配置,然而在js文件中,须要以下的配置。 if (module.hot) { module.hot.accept('./print.js', function() { console.log('Accepting the updated printMe module!'); printMe(); })}Reference:https://webpack.docschina.org/concepts/hot-module-replacement/HMR配置 babel-loader将es6代码转化为es5代码,适应各种浏览器的兼容性

November 9, 2020 · 1 min · jiezi

关于webpack:webpack入门

npm init -y 生成packge.json。npm i webpack webpack-cli -D,这时候能够npx webpack打包。packge.json文件script中退出"dev": "webpack --config webpack.config.js"新建webpack.config.js const path = require("path"); // webpack外部办法path组件module.exports = { entry: "./main.js", // 入口文件指定 output: { //进口文件配置 配置进口打包门路 filename: "index.js", //打包后的文件名称 path: path.resolve(__dirname, "dist") //resolve绝对路径引入 }};配置完这2个文件就能够间接 npm run dev打包了4.打包时主动生成index.html npm i -D html-webpack-plugin webpack.config.js中退出 // index.html生成插件const HtmlWebPackPlugin = require("html-webpack-plugin"); module.exports = { plugins: [ new HtmlWebPackPlugin({ filename: "index.html", // 生成打包文件名 template: "./index.html", // 模板门路 minify: { //生产模式能够进行配置 removeAttributeQuotes: true, //删除 html文件双引号 collapseWhitespace: true //折叠控行 }, hash:true, //增加哈希值~~~~ }) ],}5.css模块,css以行内的形式插入到head中npm i -D --save css-loader style-loaderwebpack.config.js中退出 ...

November 3, 2020 · 2 min · jiezi

关于webpack:treeshaking效果探讨

webpack等构建工具提供tree-shaking机制, 利用es6中Module的语法的export和import语法进行动态剖析,对无用代码进行剔除,缩小打包后的代码量.启动webpack的tree-shaking,须要: webpack在v2.0以上开启代码压缩webpack只是标记语句依赖以及是否应用, tree-shaking的具体实现个别是由压缩器提供实现, 如webpack默认的压缩工具 terser-webpack-plug就反对tree-shaking.本文不是探讨如何启用tree-shaking,也不钻研tree shaking的底层原理,只通过案例, 钻研tree-shaking对代码的一些影响. 演示中的webpack的配置为: const path = require('path');module.exports = { mode: 'production', entry: './src/index.js', output: { // filename: 'bundle.js', path: path.join(__dirname, 'dist') }, devtool: 'hidden-source-map',};js压缩器的选项采纳默认, 在源码中增加非凡的符号>>>来查看成果.模块接口没有相互依赖模块中的代码: export var firstName = '>>>Michael';export var lastName = '>>>Jackson';export var year = 1958;export var person = { name: '>>>joyer' };export function log(info) { console.log(`>>>${info}`);}export default function() { console.log('>>>i am default');}在入口文件(src/index.js)中, 如果导入(包含全量导入, 默认导入, 具名导入)但没有应用的话: import { firstName } from './mod1.js';console.log('>>>index.js');整个模块都会被疏忽, 打包后的要害代码: ...

October 25, 2020 · 7 min · jiezi

关于webpack:前端工程化篇三-席卷八荒Webpack基础

字数:8960, 浏览工夫:28分钟,点击浏览原文 尽前行者境地窄,向后看者眼界宽。 ——《格言联璧·持躬类》【前端工程化】系列文章链接: 01 扬帆起航-开发环境02 白璧微瑕-包管理器示例代码仓库:https://github.com/BWrong/dev-tools 申明:本篇文章基于webpack v4.43.0,如依照文中代码执行报错,请先查看依赖版本是否和示例代码仓库中统一。 前言自Web2.0以来,前端技术日益蓬勃发展,前端仔们不再满足于切切页面、写写动画,而是可能做更多"高大上"的事件了。但随着我的项目规模和复杂度的晋升,代码的依赖保护、代码压缩、代码格调审查等与业务无关但又不得不做的事件占据了开发人员越来越多的工夫。那时,这些操作均只能依附开发人员手动来进行解决,耗时耗力,齐全是一个刀耕火种的时代(从前车马很慢,毕生只够爱一个人????)。 起初,NodeJS呈现了,作为一门服务端语言,它领有更加弱小的能力,尤其是解决文件的能力,运行也不再受限于浏览器沙盒,能够间接在终端命令行运行。这些个性正是开发前端工程化的外围需要,所以有人开始借助NodeJS来做那些耗时耗力的工作,属于前端本人的工程化时代初见端倪。 当然,这里咱们的重点是Webpack,所以不会花大量篇幅去讲述前端工程化的发展史,仅仅列出一些比拟有代表性的工具,以致敬这些前浪们 Grunt:基于工作的命令行构建工具,构建工具的先驱。Gulp:管道,流式解决,构建性能比grunt高,配置也比较简单。Browserify:把Commonjs打包成浏览器反对的包。Webpack:模块打包器,通过loader反对泛滥文件类型,反对插件,反对按需加载,提取专用代码等,生态欠缺,目前最风行的打包工具。Rollup:侧重于打包库、SDK,输入成绩体积较小。Parcel:打包速度快,入口反对html,打包时会主动装置须要的插件,人家的口号是技术零配置。snowpack:打包速度快,无需打包工具。在Webpack刚刚进去的时候,那个时候Gulp和Grunt还风华正茂,在网上常常有人拿它们来做比照,其实他们是不同类型的构建工具,不是太具备可比性。 如上图所示,虽说它们都是构建工具,然而Gulp、Grunt更加偏差工作式,所有的操作均需以工作的形式来构建;而Webpack则是模块化的编译计划,它通过依赖剖析,进行模块打包,与它比照的应该是Browserify,Rollup之流。 目前来说,grunt和gulp,曾经功成身退了,当下webpack无疑是最热门、最弱小的打包工具。这都得益于保护团队海纳百川有容乃大的态度,Rollup的tree shaking、Parcel的零配置这些亮点都被webpack排汇了。兴许有的人感觉是剽窃,然而咱们读书人的世界何来剽窃呢。更何况不是这样的话,不是得学更多的工具了,又何来我这一头漆黑靓丽的秀发呢????????? 好了,啰嗦了半天,该进入主题了,接下来,看看webpack官网的定义: At its core, webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph which maps every module your project needs and generates one or more bundles. 译:Webpack 是一个古代 JavaScript 应用程序的动态模块打包器(module bundler),当 webpack 解决应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中蕴含应用程序须要的每个模块,而后将所有这些模块打包成一个或多个 bundle。 这里屡次提到了模块,模块在前端开发中个别指JavaScript的模块化产物,相干的介绍网上也有很多,切实找不到也能够看看鄙人之前的文章再谈JavaScript模块化,这里咱们不再赘述。然而这里Webpack所指的模块不仅仅是JavaScript中的模块,通过Loader,它能够解决任意类型的资源。狭义上来说,在Webpack看来,任意资源都是模块。 ...

October 19, 2020 · 7 min · jiezi

关于webpack:如何编写一个-Webpack-Plugin

前言上次写了 如何编写一个 Webpack Loader,明天来说说如何编写一个 Webpack Plugin。 webpack 外部执行流程一次残缺的 webpack 打包大抵是这样的过程: 将命令行参数与 webpack 配置文件 合并、解析失去参数对象。参数对象传给 webpack 执行失去 Compiler 对象。执行 Compiler 的 run 办法开始编译。每次执行 run 编译都会生成一个 Compilation 对象。触发 Compiler 的 make 办法剖析入口文件,调用 compilation 的 buildModule 办法创立主模块对象。生成入口文件 AST(形象语法树),通过 AST 剖析和递归加载依赖模块。所有模块剖析实现后,执行 compilation 的 seal 办法对每个 chunk 进行整顿、优化、封装。最初执行 Compiler 的 emitAssets 办法把生成的文件输入到 output 的目录中。Plugin 作用按我的了解,Webpack 插件的作用就是在 webpack 运行到某个时刻的时候,帮咱们做一些事件。 在 Webpack 运行的生命周期中会播送出许多事件,Plugin 能够监听这些事件,在适合的机会通过 Webpack 提供的 API 扭转输入后果。 官网解释是: 插件向第三方开发者提供了 webpack 引擎中残缺的能力。应用阶段式的构建回调,开发者能够引入它们本人的行为到 webpack 构建流程中。编写 Pluginwebpack 插件的组成: ...

October 18, 2020 · 2 min · jiezi

关于webpack:webpack核心概念

一,模式mode1,开发模式,development包管理工具的--dev指令,是指包装置在改位子。2,生产模式,production 二,出入口入口:所有资源的被打包的入口文件进口:管制所有入口文件打包后的位子 三,loader四,Plugin

October 17, 2020 · 1 min · jiezi

关于webpack:webpack的cleanwebpackplugin插件报错

1、出错代码const path = require('path')const CleanWebpackPlugin = require('clean-webpack-plugin')// const { CleanWebpackPlugin } = require('clean-webpack-plugin')module.exports = { entry: './input.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'output.bundle.js' }, mode: 'development', plugins: [ new CleanWebpackPlugin() ], module: { rules: [ { test: /\.(png|jpg|gif)$/i, use: [ { loader: 'url-loader', options: { // limit: 8192 // 阐明小于8192字节也就是8k才会执行 limit: 919200 } } ] } ] }}2、谬误形容 [webpack-cli] TypeError: CleanWebpackPlugin is not a constructor 3、出错起因导入插件语句有误,以及应用有误 ...

October 16, 2020 · 1 min · jiezi

关于webpack:webpack引入moment打包体积过大问题

moment 打包体积很大?在我的项目架构中,引入工夫插件moment打包体积很大,具体是什么起因呢? 在node_modules外面找到moment文件夹,找到文件moment.js文件,在文件外面搜寻loadLocale函数 这个函数会在每次打包的时候主动引入所有语言包,导致体积过大 优化计划在webpack中配置一个IgnorePlugin办法 let webpack = require('webpack');module.exports = { ... plugins: [ new webpack.IgnorePlugin(/\.\/locale/, /moment/), // 疏忽moment的语言包打包 ]}这样所有的语言包都没了,咱们要再应用中文语言包,只须要手动在主文件中引入(个别是在index.js) import 'moment/locale/zh-cn';

October 15, 2020 · 1 min · jiezi

关于webpack:一种小拖大的jssdk加载方案

背景jssdk 是在前端中实现某些业务性能的 JavaScript 函数库,通常由 sdk 的开发者开发结束后,交给业务的页面来引入应用。例如: <head> </head>在一些非凡的场景(例如联盟广告)下,咱们通常须要把一个 jssdk 地址 交付给另外一个团队的页面来引入。 技术计划种子js的实现种子 server端 的实现种子js 并不是一个动态的 js,因为它须要内置一个最新版本的资源映射表。因而他是由 server 端动静来生成的,咱们 server 端能够采纳 Node.js 配合模板引擎来实现。 通过 webpack 插件生成资源配置表通过 webpack loader 生成主js我的项目整体构造

October 11, 2020 · 1 min · jiezi

关于webpack:webpack封装配置详解

utils.js// utils.js文件次要是用来解决各种css loader的,比方css-loader,less-loader等。// 引入path模块const path = require('path')// 引入之前的config模块const config = require('../config')// 引入"extract-text-webpack-plugin"它的作用是打包后将生成的css文件通过link的形式引入到html中,如果不应用这个插件,那么css就打包到<head>的style中const ExtractTextPlugin = require('extract-text-webpack-plugin')// 引入package.jsonconst pkg = require('../package.json')exports.assetsSubDirectory = function (_path) { // 联合config文件的代码能够晓得 当环境为生产环境时 assetsSubDirectory为static开发环境也是static const assetsSubDirectory = progress.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory // path.posix.join()是path.join的一种兼容写法 它的作用是门路的拼接 比方path.posix.join('/aa/s','bb') return path.posix.join(assetsSubDirectory,_path)}// 用来生成Loader的函数,自身能够用在vue-loader的配置上,同时也是为styleLoader函数应用exports.cssLoaders = function(options){ // 如果没有传参就默认空对象 options = options||{} // 配置css-loader,css-loader能够让解决css中的@import或者url() const cssLoader = { loader:'css-loader', options:{ sourceMap:options.sourceMap } } // 配置postcss-loader,次要性能是补全css中的兼容性前缀 比方“-webkit-”等 var postcssLoader = { loader:"postcss-laoder", options:{ sourceMap:options.sourceMap } } // 生成loader的公有办法 function generateLoaders(loader,loaderOptions){ // 参数的usePostCss属性是否为true 是就应用两个loader,否则只应用css-loader const loaders = options.usePostCSS? [cssLoader,postcssLoader]:[cssLoader] if(loader){ // 给generateLoaders传loader参数的话 比方less 或者sass 就将这个loader的配置传入到loaders数组中 loaders.push({ loader:loader+'-loader', // Object.assign()是es6的语法 用来合并对象 options: Object.assign({}, loaderOptions, { sourceMap: options.sourceMap }) }) }// 如果options参数的extract属性为true 即应用extract text plugin 将css抽成独自的文件 否则就将css写进style if(options.extract){ return ExtractTextPlugin.extract({ use:loaders, // vue-style-loader能够了解为vue版的style-loader 它可将css放进style中 fallback:'vue-style-loader' }) }else{ return ['vue-style-loader'].concat('loaders') } } return { // 返回各种loader css:generateLoaders(), postcss:generateLoaders(), less:generateLoaders('less'), sass:generateLoaders('sass',{indentedSyntax:true}), scss:generateLoaders('sass'), stylus:generateLoaders('stylus'), styl:generateLoaders('stylus') }}// 生成开发环境下的loader的配置,应用在(webpack.dev.config.js中)exports.styleLoaders = function(options){ const output = [] // 调用cssLoader办法 返回loaders的对象 const loaders = exports.cssLoaders(options) // 遍历每一个loader 并配置成对应的格局 传给output数组 for(const extension in loaders){ const loader = loaders[extension] output.push({ test:new RegExp('\\.'+extension + '$'), use:loader }) } return output}

October 11, 2020 · 1 min · jiezi

关于webpack:webpack篇插件plugin开发

开发webpack插件须要晓得的几个必要条件: 获取编译器 compiler 对象,通过这个对象能过获取包含config配置,资源文件,编译信息,钩子函数等信息编译阶段的生命周期函数,找到适宜的钩子函数解决对应逻辑返回后果反对同步和异步两种形式获取compiler实例第一步获取 compiler 实例对象: // helloPlugin.jsmodule.exports = class RemoveLogs { constructor(options){ this.options = options } apply(compiler) { console.log(`Hello ${this.options.name}`) }};引入这个脚本,在控制台执行就能看到编译后果了: // webpack.config.jsvar HelloWorldPlugin = require('./helloPlugin.js');module.exports = { // 配置插件 plugins: [new HelloWorldPlugin({ name:"chenwl" })]};生命周期钩子函数通过官网文档 compiler-hooks 能够查看到compiler 提供的钩子函数,也能够间接到 /node_modules/webpack/lib/Compiler.js 查看 同步和异步形式钩子函数能够同步也能够异步的形式解决: module.exports = class SyncPlugin { apply(compiler){ // tap 同步 compiler.hooks.emit.tap("tap", (compilation) => { console.log("***** tap *****") }) // tapAsync 参数cb未调用之前过程会暂停 compiler.hooks.emit.tapAsync("tapAsync", (compilation,cb) => { start(0); function start(index){ console.log(index); if(index<=3){ setTimeout(() => { start(++index); }, 1000); }else{ cb() } } }) // tapPromise 通过promise的形式调用 compiler.hooks.emit.tapPromise("tapPromise", (compilation)=>{ return new Promise((resolve,reject)=>{ console.log("start tap-promise"); setTimeout(()=>{ resolve() },2000) }) }) }}logRemoverPlugin文件编译实现后,去掉console: ...

October 11, 2020 · 3 min · jiezi

关于webpack:webpack篇手写常见loader

loader 是导出为一个函数的 node 模块。该函数在 loader 转换资源的时候调用。给定的函数将调用 loader API,并通过 this 上下文拜访。Webpack的配置离不来 loader,官网也有对于如何编写一个loader的文档介绍,这篇文章会通过手写一些常见的loader,加深对loader的意识,进步工作中的开发效率。 导出loaderloader是一个函数,承受匹配到的文件资源字符串和SourceMap,咱们能够通过批改文件内容的字符串返回给下个一loader解决: module.exports = function(source,map){ return source;}筹备工作为了不便咱们编写loader,咱们先筹备好webpack环境: 生成一份 package.json: npm init -y装置webpack: npm install webpack webpack -D创立webpack.config.js文件,并输出以下内容: // webpack.config.jsconst path = require('path')module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }}package.json退出scripts命令: "scripts": { "build": "webpack", "dev": "webpack --watch" },配置别名咱们晓得,webpack 默认会到 node_modules 外面找对应的loader,这样不不便咱们调试,咱们能够通过给webpack.config.js增加resolveLoader属性,将loader指向咱们创立的loaders文件夹 ... resolveLoader: { modules: [ path.resolve(__dirname, "node_modules"), path.resolve(__dirname, "./loaders"), ], }... 创立loaders文件夹,外面将寄存咱们本人编写的loader: ...

October 10, 2020 · 2 min · jiezi

关于webpack:实现一个简易版Webpack

之前常常被webpack的配置搞得头大,chunk、bundle和module的关系傻傻分不清,loader和plugin越整越糊涂,优化配置一大堆,项目经理前面催,优化过后慢如龟!明天为了彻底搞明确webpack的构建原理,决定手撕webpack,干一个简易版的webpack进去! 筹备工作在开始之前,还要先理解 ast 形象语法树和了解事件流机制 tapable,webpack 在编译过程中将文件转化成ast语法树进行剖析和批改,并在特定阶段利用tapable提供的钩子办法播送事件,这篇文章 Step-by-step guide for writing a custom babel transformation 举荐浏览能够更好的了解ast。 装置 webpack 和 ast 相干依赖包: npm install webpack webpack-cli babylon @babel/core tapable -D 剖析模板文件webpack默认打包进去的bundle.js文件格式是很固定的,咱们能够尝试新建一个我的项目,在根目录下新建src文件夹和index.js及sum.js: src- index.js- sum.js- base - a.js // module.exports = 'a' - b.js // module.exports = 'b'// sum.jslet a = require("./base/a")let b = require("./base/b")module.exports = function () { return a + b}// index.jslet sum = require("./sum");console.log(sum());同时新建 webpack.config.js 输出以下配置: ...

October 9, 2020 · 4 min · jiezi

关于webpack:AST抽象语法树和Babel插件

AST(Abstract Syntax Tree, AST)形象语法树,能够把代码转译成语法树的表现形式例如上面的代码: var a = 3;a + 5AST形象进去的树结构: Program代表的是根节点 VariableDeclaration变量申明 Identifier 标识符 + Numeric Literal数字字面量BinaryExpression(二项式) Identifier 标识符,operator 二项式运算符,Numeric Literal数字字面量能够到 astexplorer.net 查看AST的解析后果 编译器过程大多数编译器的工作过程能够分为三局部: Parse(解析)Transform(转换)Generate(代码生成) 装置 esprima 来了解编译的过程: npm install esprima estraverse escodegenconst esprima = require('esprima')const estraverse = require('estraverse')const escodegen = require('escodegen')let code = `var a = 3`// Parse(解析)let ast = esprima.parseScript(code);//Transform(转换)estraverse.traverse(ast, { enter(node) { console.log("enter",node.type); }, leave(node) { console.log("leave",node.type); }});// Generate(代码生成)const result = escodegen.generate(ast);Babel 对于 AST 的遍历是深度优先遍历,对于 AST 上的每一个分支 Babel 都会先向下遍历走到止境,而后再向上遍历退出刚遍历过的节点,而后寻找下一个分支。AST 对语法树的遍历是 深度优先遍历,所以会先向下遍历走到止境,而后再向上遍历退出刚遍历过的节点,寻找下一个分支,所以遍历的过程中控制台会打印上面的信息: ...

October 6, 2020 · 3 min · jiezi

关于webpack:代理模式在vue中惰性加载echarts等第三方库

1.根底知识点(1)代理模式先从一个简略的例子动手代理模式。如代码所示 // 留神:如果在node环境下运行程序请装置node-fetch// const fetch=require('node-fetch') // 依据keyword申请数据function request(keyword=''){ return new Promise((resolve,reject)=>{ fetch(`http://example.com/data/${keyword}`) .then(response=> { resolve(response.json()) }) })}// 主体程序async function main(){ let res try{ res=await request('a') }catch(e){ } ..........}通常遇到异步申请的场景中,咱们会把异步申请逻辑写在一个独立的函数外面,如上述代码中的request函数,而后主函数main通过调用request函数获取数据。 但如果该接口的开销比拟大、调用比拟频繁多且查问的条件局部反复,咱们能够在main和request两头加一层缓存代理,在下一次调用时如果传入的参数跟之前统一,则可间接返回之前查问的后果。代码如下所示: // 依据keyword申请数据function request(keyword=''){ return new Promise((resolve,reject)=>{ fetch(`http://example.com/data/${keyword}`) .then(response=> { resolve(response.json()) }) })}// 缓存代理const requestProxy=(function(){ let cache={} return async function(keyword){ if(cache[keyword]) return cache[keyword] else{ cache[keyword]=await request(keyword) return cache[keyword] } }})()// 主体程序async function main(){ let res try{ // 第一次:通过异步获取后果 res=await requestProxy('a') // 第二次:通过缓存获取后果 res=await requestProxy('a') }catch(e){ } ..........}除此以外,代理模式的使用场景也很多,例如在img节点加载好图片之前先加载菊花图,简略来说,先加载菊花图,菊花图加载实现后登程onload回调函数,把img的src设置回本来要加载的图片。 ...

October 1, 2020 · 2 min · jiezi

关于webpack:Webpack-源码分析2-Tapable-与-Webpack-的关联

文章首发于我的博客 https://github.com/mcuking/bl...接着上文 Webpack 源码剖析(1)—— Webpack 启动过程剖析 咱们接下来持续剖析 webpack 的构建流程。 上文结尾处咱们提到了 webpack-cli 最终还是调用了 webpack 提供的 webpack 函数,取得了 compiler 实例对象。那么咱们就从新回到 webpack 包查看下这个 webpack 函数,webpack 函数所在文件是 node_module\webpack\lib\webpack.js 。上面是其中的要害代码: const Compiler = require("./Compiler");...const webpack = (options, callback) => { ... let compiler; if (Array.isArray(options)) { compiler = new MultiCompiler( Array.from(options).map(options => webpack(options)) ); } else if (typeof options === "object") { options = new WebpackOptionsDefaulter().process(options); compiler = new Compiler(options.context); compiler.options = options; ... if (options.plugins && Array.isArray(options.plugins)) { for (const plugin of options.plugins) { if (typeof plugin === "function") { plugin.call(compiler, compiler); } else { plugin.apply(compiler); } } } compiler.hooks.environment.call(); compiler.hooks.afterEnvironment.call(); compiler.options = new WebpackOptionsApply().process(options, compiler); } else { throw new Error("Invalid argument: options"); } if (callback) { ... compiler.run(callback); } return compiler;}有下面的代码咱们能够看到 webpack 函数是通过引入了内部定义好的 Compiler 类,并基于接管到的 options 初始化了一个实例对象(如果 options 是数组,则遍历数组中每个 option,别离初始化 compiler 实例对象),最初调用了 compiler 实例上的 run 办法(如果是 watch 模式则调用 watch 办法)。 ...

September 30, 2020 · 3 min · jiezi

关于webpack:Webpack-源码分析1-Webpack-启动过程分析

文章首发于我的博客 https://github.com/mcuking/bl...本文以 webpack 源码来剖析其外部的工作流程,筹备剖析的版本为 4.41.5。 首先咱们要确认的是 webpack 的执行入口文件,通过查看 node_modules 中 webpack 的 package.json 的 bin 字段(如下),咱们能够晓得入口文件是 bin 文件下的 webpack.js,即 node_modules\webpack\bin\webpack.js。 "bin": { "webpack": "./bin/webpack.js"},剖析 webpack 入口文件:webpack.js文件代码并不多,总共有 171 行,次要分为 6 个局部: 失常执行返回process.exitCode = 0;定义了一个运行某个命令的办法 runCommandconst runCommand = (command, args) => {...};定义了一个判断某个包是否装置的办法 isInstalledconst isInstalled = packageName => { try { require.resolve(packageName); return true; } catch (err) { return false; }};定义了两个 webpack 可用的 CLI:webpack-cli 和 webpack-command。其中 installed 属性就是调用了下面的 isInstalled 办法来计算的。const CLIs = [ { name: "webpack-cli", package: "webpack-cli", binName: "webpack-cli", alias: "cli", installed: isInstalled("webpack-cli"), recommended: true, url: "https://github.com/webpack/webpack-cli", description: "The original webpack full-featured CLI." }, { name: "webpack-command", package: "webpack-command", binName: "webpack-command", alias: "command", installed: isInstalled("webpack-command"), recommended: false, url: "https://github.com/webpack-contrib/webpack-command", description: "A lightweight, opinionated webpack CLI." }];紧接着计算出曾经装置的 CLIconst installedClis = CLIs.filter(cli => cli.installed);而后依据装置 CLI 的数量进行解决if (installedClis.length === 0) {...}else if(installedClis.length === 1) {...}else {...}如果一个都没有装置,则会提醒是否要装置 webpack-cli,如果批准则主动帮你装置;如果装置了其中一个,会间接应用那个;如果装置了俩个,会提醒你删掉其中一个 CLI。 ...

September 30, 2020 · 3 min · jiezi

关于webpack:HMR知识点梳理

HMR是啥?简略来讲就是开发的时候更新代码不必刷新整个页面就能够更新对应的内容,能够进步开发效率 webpack中实现HMR所依赖的api与技术性能点nodejs的监听文件的apihttp://nodejs.cn/api/fs.html#fs_fs_watchfile_filename_options_listener,用于监听文件变动内存文件系统https://github.com/webpack/webpack-dev-middleware/blob/v3.7.0/lib/fs.js#L115,用内存文件系统替换compiler自带的文件系统,能够大大提高效率及时通信,生成新的代码块后被动告诉客户端模块中须要解决HMR的事件,个别在loader中解决,能够参考vue-loader的https://github.com/vuejs/vue-loader/blob/6a05115ddf3ea680ab2b00862b2891da2e98a41c/lib/codegen/hotReload.js,如果对应模块中没有解决HMR的代码,事件会一层层冒泡直至刷新整个页面

September 30, 2020 · 1 min · jiezi

关于webpack:webpack实战入门

webpack 1. webpack 就是一个js程序的打包器,当 webpack 解决应用程序时,它会递归地构建一个依赖关系图3.webpack提供了模块化反对,代码压缩混同,解决js兼容问题,性能优化等个性,进步了开发效率和我的项目的可维护性 2. webpack打包的益处程序员在开发的时候须要更好的代码布局,比方有空格、有换行、有正文、错落有致浏览器望能更快的解析代码,而不是更好的看懂代码。所以Webpack的次要作用就是压缩、优化咱们写的代码,把多余的货色去掉,而后依照浏览器喜爱的格调来编排代码!webpack的应用 1. 新建我的项目空白目录 运行 npm init -y命令 初始化包治理配置文件 package.json2. 新建src源代码目录,将我的项目的源代码放在这个目录下3. 装置webpack npm i webpack webpack-cli -D - 在package.json文件的devDependencies中呈现了装置的webpack和webpack-cli,就代表装置胜利3. 在我的项目的根目录自行创立 webpack.config.js 文件 (所有的配置都写这里)配置前打包操作演示 1. 批改我的项目中的package.json文件增加运行脚本dev如下 "scripts":{ "dev":"webpack" }2. 在演示运行的时候须要初始初始配置打包模式 module.exports = { mode: 'development' // 能够设置为development(开发模式),production(公布模式) } - 如果设置为development则示意我的项目处于开发阶段,不会进行压缩和混同,打包速度会快一些 - 如果设置为production则示意我的项目处于上线公布阶段,会进行压缩和混同,打包速度会慢一些 - scripts节点下的脚本,能够通过npm run运行 如 npm run dev 将会启动webpack进行我的项目打包3. 期待webpack打包结束之后,找到默认的dist门路中生成的main.js文件,将其引入到html页面中。浏览页面查看成果。开始配置 1. 在webpack 4.x 中,默认会将src/index.js 作为默认的打包入口js文件2. 默认会将dist/main.js 作为默认的打包输入js文件// 在webpack.config.js内,咱们须要将所有配置当成一个对象导出 引入node.js 中专门操作门路的模块const path = require('path') module.exports = { ...

September 19, 2020 · 2 min · jiezi

关于webpack:webpack3升级4

webpack3降级4 Cannot read property 'Consumer' of undefined

September 17, 2020 · 1 min · jiezi

关于webpack:实现一个-webpack-loader-和-webpack-plugin

loader官网上的定义: loader 是一个转换器,用于对源代码进行转换。例如 babel-loader 能够将 ES6 代码转换为 ES5 代码;sass-loader 将 sass 代码转换为 css 代码。 个别 loader 的配置代码如下: module: { rules: [ { test: /\.js$/, use: [ // loader 的执行程序从下到上 { loader: path.resolve('./src/loader2.js'), }, { loader: path.resolve('./src/loader1.js'), }, ] } ] },rules 数组蕴含了一个个匹配规定和具体的 loader 文件。 上述代码中的 test: /\.js$/ 就是匹配规定,示意对 js 文件应用上面的两个 loader。 而 loader 的解决程序是自下向上的,即先用 loader1 解决源码,而后将解决后的代码再传给 loader2。 loader2 解决后的代码就是最终的打包代码。 loader 的实现loader 其实是一个函数,它的参数是匹配文件的源码,返回后果是解决后的源码。上面是一个最简略的 loader,它什么都没做: module.exports = function (source) { return source}这么简略的 loader 没有挑战性,咱们能够写一个略微简单一点的 loader,它的作用是将 var 关键词替换为 const: ...

September 11, 2020 · 3 min · jiezi

关于webpack:关于babel的一点学习

1.babel-core当咱们在webpack中应用babel的时候,首先要装置babel-core,这是babel编译库的外围包 npm install babel-core --save-dev2.babel-loader之后,webpack中对js文件,咱们要进行编译,就须要配置,在webpack中,须要用到babel-loader来应用bebel npm install babel-loader --save-dev所以,在webpack.config.js代码中,要这样写: rules: [ { test: /\.js$/, use: { loader: 'babel-loader' }, exclude: '/node_modules/' }]3.babel-preset-xxx(xxx代表很多选项)(1)babel-preset-es2015依照es6规范进行编译,同理,如果依照es7规范进行编译,则装置babel-preset-es2016 (2)babel-preset-env一般来说,如果你想用最新的标准做编译,间接装置babel-preset-env就能够了,它蕴含了 babel-preset-es2015, babel-preset-es2016, and babel-preset-es2017,等价于babel-preset-latest,能够编译所有最新标准中的代码 应用编译规定来配置webpack,在babel-loader中新增配置参数: rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: ['env'] //也能够写成presets: ['babel-preset-env'] }, exclude: '/node_modules/' } }]4.babel-polyfillbabel官网上写了很明确一句话,babel只负责对语法进行编译。当咱们写箭头函数,babel会帮你把它编译成一般函数,这没有任何问题,然而,比如说咱们代码里应用了promise,babel打包进去的代码其实还是promise,在低版本浏览器里,promise并不反对,然而babel并不会帮你解决,因为这不是语法编译层面须要做的事件。不转换新的API包含,比方Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象。 于是,如果咱们要让打包进去的代码能兼容低版本浏览器,还要思考到promise,Set这样的新语法低版本浏览器不兼容的问题,这时候babel-polyfill就出场了。你只须要全局装置一下babel-polyfill: npm install --save-dev babel-polyfill而后在我的项目中应用一下它,代码就不存在方才提到的兼容性问题了: import 'babel-polyfill';然而,间接用babel-polyfill会有一些坑,第一个坑是净化全局环境,比如说低版本浏览器没有Set,然而babel-polyfill会在全局变量里加一个Set。再一个问题是,会造成代码冗余,举个例子,多个模块用到Promise,每个模块里都有可能独立存在一个对Promise做兼容性的代码。所以,应用babel-polyfill能够解决兼容性问题,但并不是最佳计划,于是,呈现了babel-plugin-transform-runtime,应用这个插件,就能够解决下面的问题了。 5.babel-plugin-transform-runtime装置: npm install babel-plugin-transform-runtime --save-devnpm install babel-runtime --save-dev更改webpack的配置: rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: ['env'], //也能够写成presets: ['babel-preset-env'] plugins: ['transform-runtime'] }, exclude: '/node_modules/' } }].babelrc文件============== ...

September 11, 2020 · 1 min · jiezi

关于webpack:初识webpack40基础原理

前言之前都是应用,没有零碎的残缺的好好学习过一遍webpack的原理,趁现在工作有些闲空,整顿下。增强下记忆什么是webpack?不想放webpack官网那张图,感觉都快看吐了。算了还是斗争把,要不然不好说分明.....官网的哈,看能看出的来,那持续了~~ 分为三个局部左侧: 写我的项目的源代码,源文件左侧有一个入口,最下面的.js一个一个的方格能够了解成一个一个的模块,因为webpack是基于node的,所以能够用模块的概念来辨别文件。箭头的示意➡️一个模块能够依赖多个模块箭头➡️示意他们之间的依赖关系左侧会有很多,后缀的文件是浏览器无奈辨认的。例如:.sass .hbs .cjs所以须要通过webpack 来构建一次打包通过webpack的构建 右侧: 输入浏览器能够正确执行的文件,资源咱们还能够利用webpack来压缩 js 、压缩图片 。所以webpack就是一个模块打包器 webpack装置分为两种装置形式 我的项目装置(举荐) 有些我的项目可能很老,依赖的webpack的版本比拟低。 最新的是webpack4.X; webpack5.x还没有公布全局装置如果是全局装置,版本会固定化。如果是去批改来的版本会抵触,依赖抵触等。所以依据我的项目来装置webpack,会好些。装置:webpacl4.x之后,这两个必须要都装置,webpack和 webpack-cli;不倡议离开装。 创立我的项目之后,现须要npm init一下,之后在装置。初始化一下。这个大家应该都晓得。 npm install webpack webpack-cli -Dwebpack启动webpack4.0公布的时候,有说,反对零配置启动。后果发现,哈哈哈哈哈哈哈哈~~~没啥用。根本用不到我的项目中。 咱们来看下零配置:装置好webpack之后,咱们来创立一个入口文件,index.js 零配置 : 默认入口:./src/index.js如果没有src 上面的index.js 打包是不胜利的。默认进口:输入的目录是 ./dist,输入的资源名称:main.js接下来,启动webpack,启动形式有两种。 第一种启动形式:npx webpack为啥用npx启动我的项目 npm5.2版本,就自带npx这个性能;当初咱们启动的整个我的项目webpack是部分装置的如果全局没有装置webpack,全局执行 webpack 就会报错,command not found。它会去全局环境变量中去寻找。通过npx 启动webpack,它会在以后我的项目下的node_modules/.bin 目录去找它的软连贯,在.bin 目录下找到webpack,去启动它。 来看下,打包好外面都显示了啥? 构建的哈希版本Hash: 096bb4bcb72f81514554执行构建的webpack版本Version: webpack 4.44.1执行构建的时长Time: 217ms结构的内容是什么?// 输入的资源叫main.js// 这个资源是958 bytes// Chunks 是为 0 Built at: 2020-08-25 14:45:08 Asset Size Chunks Chunk Namesmain.js 958 bytes 0 [emitted] main// 这块示意 输入的main.js,是依据./src/index.js文件built而来的Entrypoint main = main.js[0] ./src/index.js 29 bytes {0} [built]正告内容本人百度吧,好吗? 用我小学的英语水平大略就是:环境没有设置:就是生产环境和开发环境没设置。会走一个默认的值,默认值是production(生产模式); ...

September 9, 2020 · 3 min · jiezi

关于webpack:前端开发中常用的webpack优化和相关原理

webpack优化方向对于webpack的优化,通常咱们是划分为开发体验和输入代码品质两方面来思考的 ( 一 )优化开发体验通常是从优化构建速度和应用体验 【开发环境】放大文件的搜寻范畴(loader配置includeexclude、module.noParse疏忽对局部没有采纳的模块话的文件递归解析,resolve modules间接指定第三方模块的门路) 主动刷新(设置watch:true,然而会制动刷新浏览器)热更新(网页不刷新,状态不会失落module.hot) DllPlugin (对于依赖的第三方库打包成动态链接库)【生产环境】优化bable-loader (开启缓存)IgnorePluginnoParsehappyPack (多线程打包)ParallelUglifyPlugin(多线程打包)( 2 )优化输入品质【缩小用户能感知到的加载 工夫,也就是首屏加载工夫】辨别环境压缩代码 (js css 图片)tree-shaking (除去没有用到的代码)提取公共代码 (多线程打包)宰割代码按需加载(多线程打包)CDN减速【晋升晦涩度,也就是晋升代码性能】Scope Hosting(让文件打包更小,运行更快)prepack(编译代码是提前将计算结果放到编译后的代码中,再间接运行后果输入)简析相干原理主动刷新让webpack 开启文件监听模式,只须要把wacth:true,wacthOptions能够设置不监听的文件 文件监听原理文件监听原理,是定时获取这个文件最初编辑的工夫,每次都存下最新的最初编辑工夫,如果发现以后获取和最初一次保留的编辑工夫不统一,认为文件产生了变动多个文件的监听,webpack会从入口文件登程,递归所有依赖文件,将这些依赖文件都退出到监听列表中保留文件和最初的编辑工夫占内存,缩小监听文件数量和升高检测频率主动刷新的原理借助浏览器拓展去通过浏览器提供的接口刷新向开发的网页注入代理客户端代码,通过代理客户端去刷新正个页面热更新劣势实时预览不刷新浏览器,能够保留以后网页的运行状态原理在我的项目中注入一个代理客户端来连贯DevServer和我的项目DevServer在每次批改文件后,会生成一个用于替换老模板的补丁文件hot-update.js结尾,同时浏览器开发者工具也能够看到申请这个补丁包但在编辑main.js的时候会发现整个网页刷新了,起因是在子模块产生更新的时候,更新事件会一层层向上传递,到最外层没有文件接管它,则会刷新网页css文件没有中央接管,然而批改所有的css文件都会触发热更新,起因是在于style-loader会注入接管css的代码DllPlugin对于依赖的第三方库,比方vue,vuex等这些不会批改的依赖,咱们能够让它和咱们本人编写的代码离开打包,这样做的益处是每次更改我本地代码的文件的时候,webpack只须要打包我我的项目自身的文件代码,而不会再去编译第三方库,那么第三方库在第一次打包的时候只打包一次,当前只有咱们不降级第三方包的时候,那么webpack就不会对这些库去打包,这样的能够疾速的进步打包的速度 将我的项目依赖的根底模块抽离进去,打包到一个个独自的动态链接库中当须要导入的模块存在于某个动态链接库中,这个模块不能再被打包,间接再动态链接库中获取我的项目依赖的所有动态链接库都须要被加载bable-loader作用是辨认es6+ 的语法通过js词法解析器进行解析,失去AST,而后进行遍历通过EStree标准生成新的AST,而后通过生成器转换es5代码局部新增的原型办法(peoxy,set)babel是不会转译的须要引入 polyfil解决cacheDirectory,开启缓存,防止之后的每次执行,可能产生的、高性能耗费的Bable从新编译过程happyPack(多过程构建,缩小总构建工夫)在webpack和loader之间多加了一层,webpack到了须要编译某个类型的资源模块之后,将该资源工作解决交给了HappyPack,由它在外部线程池中进行任务调度,调配一个线程调用解决改类型资源的Loader来解决这个资源 如果js和css文件的化,间接再module,rules,use['happyPack/loader?id=css']也能够在 plugins里配置,new HappyPack({id:css}),id标识文件类型默认过程是3个,threads能够设置webpack-parallel-uglify-plugin并行处理多个子工作,多个子工作实现后,再将后果发到主过程中,会开启多个子过程,对多个js文件压缩工作分舵多个子过程去实现,能够删除所有的正文,console.log,提取呈现屡次,然而没有定义成变量的利用动态值 uglifyJS原理,将代码解析成AST语法树,再用各种规定去解决它在pulfgins里new一个 parallel-uglify,uglifyJS配置输入紧凑,和删除所有正文,compress删除console.log一次用到的变量等Tree-shaking实质上是打消我的项目中不必要的代码,摇掉没有应用的模块称为DCE,达到删除无用的代码目标,依赖ES6的模块个性 DCEdead code eliminatiom代码不会被执行,不可达到代码执行的后果不会用到代码只会影响变量(只写不读)ES6模块的特点只能作为模块顶层的预计呈现import的模块名只能是字符串常量对模块的引入是动态剖析的,所以能够在编译的时候判断到底加载了什么代码,分析程序流,判断那些变量为被应用和援用,进而删除代码毛病只对ES6+的模块化语法失效,要在babelrc里敞开babel的模块转换性能js文件里,import 一个资源,而后函数没有被应用,import不会去掉提取公共代码起因:雷同的资源重复加载,节约用户的流量和服务器老本,资源态度导致首加载屏迟缓益处:缩小网络传输量,升高服务器老本怎么提取 所有的页面须要用到的根底库,提取到一个独立的base.js文件(长缓存,动态文件名会附加文件内容计算解决的Hash值,通常不)再找到所有页面依赖的公共局部的代码,提取到common.js中为每个网页都生成独立的文件,不蕴含以上局部,各个页面独自须要的局部代码webpack内置了commonschunkPlugin懒加载(按需加载)将整个网站划分为一个个小性能每个类合并为一个chunk,按需加载对应的chunk不须要加载的用户首次关上网站是须要看到的画面对应的性能,将其放在执行入口所在的chunk中,缩小用户感知的网页加载工夫let TaskBtn = () => import(/ webpackChunkName: 'task-btn' / '@/components/TaskBtn.vue');开启 Scope Hoisting让webpack打包进去的代码文件更小,运行更快 代码体积更小,函数申明语句会产生大量的代码代码在运行时创立的函数作用域变少,内存开销也变小原理:剖析模块之间的依赖关系,尽可能将打散的模块合并到一个函数中,但前提是不能造成冗余的代码,只有援用了一次的模块能力被合并CDN减速内容散发,减速网络传输,放慢资源获取的速度动态资源的文件名须要带上由文件内容算进去的Hash值,以防被缓存不同类型的资源放到不同的域名cdn服务上,以防资源并行加载被阻塞

September 2, 2020 · 1 min · jiezi

关于webpack:webpack中的treeshaking

1.什么是tree-shaking?顾名思义,就是摇树,抖掉一些没用的代码;将一些不可能执行到的代码从文件中去除,达到放大文件体积,优化加载速度的成果 2.webpack中为什么要用es6的模块办法import/export能力tree-shaking?因为tree-shaking依赖于es6模块的动态剖析,通过动态代码的剖析就晓得模块的依赖关系 3.什么是动态代码剖析?简略来说,就是不须要运行代码;所以es6的import/export只能作为模块顶层的语句呈现,模块名称不能够有字符串拼接,不能呈现在条件判断中。。。

August 31, 2020 · 1 min · jiezi

关于webpack:webpack-之开发-source-map

当 webpack 打包源代码时,可能会很难追踪到谬误和正告在源代码中的原始地位。例如,如果将三个源文件(a.js, b.js 和 c.js)打包到一个 bundle(bundle.js)中,而其中一个源文件蕴含一个谬误,那么堆栈跟踪就会简略地指向到 bundle.js。这并通常没有太多帮忙,因为你可能须要精确地晓得谬误来自于哪个源文件。 为了更容易地追踪谬误和正告,JavaScript 提供了 source map 性能,将编译后的代码映射回原始源代码。如果一个谬误来自于 b.js,source map 就会明确的通知你。咱们先来看看不增加此配置,页面报错所指向的源文件问打包后的js文件当初咱们增加上打包之后,再看报错指向的源文件

August 6, 2020 · 1 min · jiezi

关于webpack:webpack配置之resolve

webpack配置之resolve配置extensions作用在导入语句没带文件后缀时,Webpack 会主动带上后缀后去尝试拜访文件是否存在。 extensions: ['.js', '.json','.vue', '.less'] // import引入文件的时候不必加后缀mainFields作用有一些第三方模块会针对不同环境提供几分代码。 例如别离提供采纳 ES5 和 ES6 的2份代码,这2份代码的地位写在  package.json  文件里,如下: { "jsnext:main": "es/index.js",// 采纳 ES6 语法的代码入口文件"main": "lib/index.js" // 采纳 ES5 语法的代码入口文件}Webpack 会依据  mainFields  的配置去决定优先采纳哪份代码, mainFields  默认如下: mainFields: ['browser', 'main'] Webpack 会依照数组里的程序去 package.json  文件里寻找,只会应用找到的第一个。 如果你想优先采纳 ES6 的那份代码,能够这样配置: mainFields: ['jsnext:main', 'browser', 'main']### exforceExtension作用如果配置为 true 所有导入语句都必须要带文件后缀### enforceModuleExtension作用### modules作用通知 webpack 解析模块时应该搜寻的目录。绝对路径和相对路径都能应用,然而要晓得它们之间有一点差别。通过查看当前目录以及先人门路(即 `./node_modules`, `../node_modules` 等等), 相对路径将相似于 Node 查找 'node\_modules' 的形式进行查找。应用绝对路径,将只在给定目录中搜寻。**webpack.config.js**module.exports = { //... resolve: { modules: ['node_modules'],},}; **如果你想要增加一个目录到模块搜寻目录,此目录优先于 **`node_modules/` 搜寻:**webpack.config.js**const path = require('path'); ...

August 6, 2020 · 1 min · jiezi

关于webpack:a-preliminary-understanding-of-webpack

`html-webpack-plugin`: 形容: `webpack`打包时,创立一个 `html` 文件,并把 `webpack` 打包后的动态文件主动插入到这个 `html` 文件当中 装置:yarn add html-webpack-plugin -D 应用示例: ` const HtmlWebpackPlugin = require('html-webpack-plugin') plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', template: 'template.html }) ] ``uglifyjs-webpack-plugin`: 形容:插件用来放大(压缩优化)js文件 装置: yarn add uglifyjs-webpack-plugin -D 应用示例: ` const UglifyJsPlugin = require('uglifyjs-webpack-plugin') optimization: { minimizer: [new UglifyJsPlugin()] } ``clean-webpack-plugin`: 形容:革除打包之后多余的, 不确定的文件 装置:yarn add clean-webpack-plugin -D 应用示例: const { CleanWebpackPlugin } = require('clean-webpack-plugin') plugins: [ new CleanWebpackPlugin() ]`copy-webpack-plugin`: 形容: 动态资源打包原样输入 装置: yarn add copy-webpack-plugin -D 应用示例: const CopyWebpackPlugin = require('copy-webpack-plugin') plugins: [ new CopyWebpackPlugin({ patterns: [ { from: path.join(\_\_dirname, 'assets'), to: 'assets' } ] }) ] `optimize-css-assets-webpack-plugin`: 形容:用于优化或者压缩CSS资源 装置:yarn add optimize-css-assets-webpack-plugin -D 应用示例: ` const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') plugins: [ new OptimizeCssAssetsPlugin() ] ``terser-webpack-plugin`: 形容:用于压缩javascript 装置:yarn add terser-webpack-plugin -D 应用示例: ` const TerserPlugin = require('terser-webpack-plugin') optimization: { minimizer: [new TerserPlugin()] } ` ` 生产环境下(默认配置): optimization: { minimizer: [new TerserPlugin({ terserOptions: { warnings: false, compress: { warnings: false, drop_console: false, dead_code: true, drop_debugger: true }, output: { comments: false, beautify: false }, mangle: true }, parallel: true, sourceMap: false })], }, ``mini-css-extract-plugin`: 形容:将CSS提取为独立的文件的插件 装置:yarn add mini-css-extract-plugin -D 应用示例: ` const MiniCssExtractPlugin = require('mini-css-extract-plugin') module: { rules: [ { test: /\\.(scss|sass|css)$/, use: \[MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'\] } ] } ``webpack-node-externals`: 形容:当在Webpack中捆绑时,它将疏忽node_modules 装置: yarn add webpack-node-externals -D 应用示例: ` const nodeExernals= require('webpack-node-externals') externals: [nodeExernals()] ``热加载`: 应用示例: ` const webpack = require('webpack') plugins: [ new webpack.HotModuleReplacementPlugin() ] ``用于创立编译时 “配置的全局常量”  以不便进行 环境转换`: 应用示例: ` const webpack = require('webpack') plugins: [ new webpack.DefinePlugin({ 'process.env': { `NODE_ENV: (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'prod') ? "'production'" : "'development'"` } }) ] ``webpack-merge`: 形容:webpack合并 装置 yarn add webpack-merge -D 应用示例: ` const webpackMerge = require('webpack-merge') const baseWebackConfig = require('./webpack.config.base') const webpackConfig = webpackMerge(baseWebackConfig, { mode: 'development', devtool: 'eval-source-map', stats: { children: false } }) module.exports = webpackConfig `

August 4, 2020 · 2 min · jiezi

关于webpack:webpack-安装和配置

webpack 根底篇npm 卸载包npm uninstall 包名 npm unistall webpack -g -global 全局示意哪都能用 初始化我的项目 yarn init -y ||npm init -ywebpack 装置webpack4.0装置本地的 webpackyarn add webpack webpack-cli -D||npm install webpack webpack-cli -D-D 示意 development 开发环境webpack 能够进行 0 配置目录构造 src index.js间接运行 npx webpack打包工具 -> 输入后的后果(js 模块)打包(间接 js 的模块化)手动配置 webpack(0配置很弱)[x] 默认配置文件的名字是 webpack.config.js/webpackfile.js 通常应用 webpack.config.jswebpack 是基于 node 编写的有webpack.config.js运行命令就会走咱们本人写的配置--config 来指定webpack运行哪个文件* 开发服务器配置- yarn add webpack-dev-server -D||npm install webpack-dev-server -DdevServer:{ port:3000, #端口号 contentBase:'./dist', #目录 如果没有dist文件夹 会在内存外面主动创立 open:true, #是否主动关上浏览器 progress:true, #显示进度条 compress:true #是否开启gzip压缩 proxy:{ //能够配置跨域 } }* 配置脚本命令 package.json"scripts": { } 这外面配置的命令叫做脚本-- config 指定默认运行文件是哪个"build": "webpack --config webpack.config.js", npm run build = npx webpack 会打包"dev": "webpack-dev-server" npm run dev 会启动一个服务器 默认会关上localhost:8080"start":"npm run dev"这样就能够通过 npm run dev/npm run build 执行相干的命令* 配置进口入口[x]entry 入口 能够是相对路径[x]output 进口 输入path 输入门路 必须是绝对路径 打包过后的文件夹名称filename:打包当前的文件名称module.exports={ entry:'./src/index.js', output:{ path:path.resolve(__dirname,'dist'), filename:'bundle[hash:6].js', publicPath:'http://www.baidu.com' }}间接给文件加 hash 值(避免浏览器缓存)filename:'bundle[hash].js'能够用:前面跟数字设置hash值的长度filename:'bundle[hash:8].js'* 配置打包环境mode 的值 2 个值 development 和production[x] development 开发环境 代码没压缩 有正文[x] production 生产环境 代码压缩 没有正文module.exports={ mode:'development', ...}解决 html 主动引入jsplugin 插件 plugins 插件们 数组[插件1,插件2,插件3]src ...

August 1, 2020 · 7 min · jiezi

关于webpack:webpack-安装-实用

要装 webpack 了 顺便做个教程 webpack 介绍概念:webpack 是一个古代 JavaScript 应用程序的动态模块打包器(module bundler)。当 webpack 解决应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中蕴含应用程序须要的每个模块,而后将所有这些模块打包成一个或多个 bundle。 中文网址:https://www.webpackjs.com英文网址:https://webpack.js.orgwebpack 装置npm init 回车 回车 回车 回车 回车 回车在命令行中打入npm init 之后 始终点回车 点到不能点为止 npm install --sava-dev webpack输出之后 等就能够了。。。如果比拟闲的话 能够关上文件目录 眼巴巴看着它加文件 装插件 ——— 去官网复制 (慈母般的微笑)点它 点它 点它 中文网址:https://www.webpackjs.com 再点中文文档 (英文的也行 反正我看不懂) 再点右边的目录 再创立命令中所需的文件创立 src 文件夹 文件夹里在放入一个空的 inder.html

July 22, 2020 · 1 min · jiezi

关于webpack:Webapck配置详解

webpack 1Webapck 2Webpack之编写⼀个Loader、Plugins和webpack的打包原理剖析Webpack 4玩转 webpack,使你的打包速度晋升 90%跟着"呆妹"来学webpack(根底篇)霖呆呆向你发动了多人学习webpack-构建形式篇(2)霖呆呆的六个自定义Webpack插件详解-自定义plugin篇(3)

July 21, 2020 · 1 min · jiezi

Webpack-打包太慢来试试-Bundleless

简介: Webpack 将各个资源打包整合在一起造成 bundle,当资源越来越多时,打包的过程也将越来越慢。如果咱们不须要打包呢?让浏览器间接加载对应的资源,是否就能够实现质的晋升?本文分享基于浏览器的 ESModule 能力实现 Bundless 本地开发的相干思路、核心技术点和 Vite 的相干实现,以及在阿里供应链 POS 场景下的落地实际。 一 引言Webpack 最后是为了解决前端模块化以及应用 Node.Js 生态的问题而呈现,在过来的 8 年工夫里,Webpack 的能力越来越弱小。 但因为多了打包构建这一层,随着我的项目的增长,打包构建速度越来越慢,每次启动都要期待几十秒甚至几分钟,而后启动一轮构建优化,随着我的项目的进一步增大,构建速度又会升高,陷入一直优化的循环。 在我的项目达到肯定的规模时,基于 Bundle 的构建优化的收益变得越来越无限,无奈实现质的晋升。咱们从另一个角度思考,webpack 之所以慢,次要的起因还是在于他将各个资源打包整合在一起造成 bundle,如果咱们不须要 bundle 打包的过程,间接让浏览器去加载对应的资源,咱们将有可能能够跳出这个循环,实现质的晋升。 在 Bundleless 的架构下,咱们不再须要构建一个残缺的 bundle,同时在批改文件时,浏览器也只须要从新加载单个文件即可。因为没有了构建这一层咱们将可能实现以下的指标: 极快的本地启动速度,只须要启动本地服务。极快的代码编译速度,每次只须要解决单个文件。我的项目开发构建的工夫复杂度始终为 O(1),使得我的项目可能持续保持高效的构建。更加简略的调试体验,不再强依赖 sourcemaps 即可实现稳固的单文件的 debug。基于以上的可能性 Bundleless 将从新定义前端的本地开发,让咱们从新找回前端在 10 年前批改单个文件之后,只须要刷新即可即时失效的体验,同时叠加上前端的 HotModuleReplace 相干技术,咱们能够把刷新也省去,最终实现保留即失效。 实现 Bundleless 一个很重要的根底能力是模块的动静加载能力,这一次要的思路会有两个: System.js 之类的 ES 模块加载器,益处是具备较高的兼容性。间接利用 Web 规范的 ESModule,面向未来,同时整体架构也更加简略。在本地开发过程中兼容性的影响不是特地大,同时 ESModule 曾经笼罩了超过 90% 的浏览器,咱们齐全能够利用 ESModule 的能力让浏览器自主加载须要的模块,从而更加低成本同时面向未来实现 Bundleless。 社区中在近一两年也呈现了很多基于 ESModule 的开发工具,如 Vite、Snowpack、es-dev-server 等。本文将次要分享基于浏览器的 ESModule 能力实现 Bundless 本地开发的相干思路、核心技术点以及 Vite 的相干实现和在供应链 POS 场景下的落地实际。 ...

July 10, 2020 · 3 min · jiezi

揭秘webpack插件的工作原理

webpack系列1:常见 loader 源码简析,以及动手实现一个 md2html-loaderwebpack系列2:揭秘webpack 插件工作原理webpack系列3:webpack 主流程源码阅读以及实现一个 webpack前言通过插件我们可以扩展webpack,在合适的时机通过Webpack提供的 API 改变输出结果,使webpack可以执行更广泛的任务,拥有更强的构建能力。本文将尝试探索 webpack 插件的工作流程,进而去揭秘它的工作原理。同时需要你对webpack底层和构建流程的一些东西有一定的了解。 想要了解 webpack 的插件的机制,需要弄明白以下几个知识点: 一个简单的插件的构成webpack构建流程Tapable是如何把各个插件串联到一起的compiler以及compilation对象的使用以及它们对应的事件钩子。插件基本结构plugins是可以用自身原型方法apply来实例化的对象。apply只在安装插件被Webpack compiler执行一次。apply方法传入一个webpck compiler的引用,来访问编译器回调。 一个简单的插件结构:class HelloPlugin { // 在构造函数中获取用户给该插件传入的配置 constructor(options) {} // Webpack 会调用 HelloPlugin 实例的 apply 方法给插件实例传入 compiler 对象 apply(compiler) { // 在emit阶段插入钩子函数,用于特定时机处理额外的逻辑; compiler.hooks.emit.tap('HelloPlugin', (compilation) => { // 在功能流程完成后可以调用 webpack 提供的回调函数; }) // 如果事件是异步的,会带两个参数,第二个参数为回调函数,在插件处理完任务时需要调用回调函数通知webpack,才会进入下一个处理流程。 compiler.plugin('emit', function (compilation, callback) { // 支持处理逻辑 // 处理完毕后执行 callback 以通知 Webpack // 如果不执行 callback,运行流程将会一直卡在这不往下执行 callback() }) }}module.exports = HelloPlugin安装插件时, 只需要将它的一个实例放到Webpack config plugins 数组里面: ...

June 24, 2020 · 6 min · jiezi

webpack-基础使用技巧

一、什么是webpackwebpack 是前端的一个项目构建工具,它是基于 Node.js 开发出来的一个前端工具; https://www.shangmayuan.com/a/1039b79c72014ad9b298681c.html http://webpack.github.io/ css 二、安装1)全局安装 npm i webpack -g 2)项目根目录安装到项目依赖中 npm i webpack --save-devhtml 三、使用 1)npm init -y 2)npm install jquery -S 3)webpack .\src\main.js .\dist\bundle.js前端 npm i webpack-dev-server -D 把这个工具安装到项目的本地开发依赖vuenpm i html-webpack-plugin -Dnodenpm i style-loader css-loader -Djquerynpm i less -D npm i less-loader -Dwebpacknpm i sass -D npm i sass-loader -D npm i node-sass -Dgit9)npm i babel-core babel-loader babel-plugin-transform-runtime -D npm i babel-preset-env babel-preset-stage-0 -Dgithub ...

June 24, 2020 · 6 min · jiezi

webpack

1、webpack.devServer配置如果要启用webpack代理,则配置如下: webpackConfig.devServer = { hot: true, hotOnly: false, // If true: Enable HMR without page refresh when build failure open: true, // auto open port: 8080, overlay: true, publicPath: '/', proxy: { '(/crmp/admin/**)': { target : 'http://10.6.183.146:8088/', changeOrigin: true, secure: false, // 接受运行在 HTTPS 上,且使用了无效证书的后端服务器 }, }, quiet: true, }proxy中的代理请求地址必须是相对路径,且从相对路径左侧开始匹配,如 (/crmp/admin/xxx)代理的是 http://localhost:8080/crmp/admin/xxx请求路径,代理到后端http://10.6.183.146:8088/crmp/admin/xxx地址。代理后,我们在浏览器网络栏看到的请求地址是:Request URL:http://localhost:8080/crmp/admin/xxx。实际是代理服务器请求了后端接口,再把数据响应给localhost:8080。 2、搭建基于webpack4+vue2.6的多页框架骨架

June 22, 2020 · 1 min · jiezi

自己写的webpack插件用于本地打包后将文件上传至阿里云OSS

该 Webpack 插件用于在本地打包完成后,将打包后的文件上传至 阿里云OSS,并提供上传完成的回调使用安装 webpack-oss-upload-plugin npm install webpack-oss-upload-plugin -D在 webpack config 中使用 const prefix = `${dir}/${projectName}/${version}/`;{ output:{ publicPath: `http://e-package.oss-cn-shanghai.aliyuncs.com/${prefix}` }, plugins: [ new WebpackOssUploadPlugin({ // oss 的配置 oss: { region: 'region', endpoint: 'endpoint', accessKeyId: 'accessKeyId', accessKeySecret: 'accessKeySecret', bucket: 'bucket' }, // 上传后的文件路径为:publicPath/{prefix}/your-file.js prefix, // 上传完成后会调用该回调 onComplete: (complication) => { } }) ]}在 onComplete 的参数暴露了 complication 对象,里面包含当前打包的信息,你可以合理使用它 选项说明 oss: 阿里 oss 配置,region、endpoint、accessKeyId、accessKeySecret、bucket 这些参数是必须的dir:可选项,默认为空数组,数组中的每一项表示上传至 oss 形成的目录名。比如 prefix: a/c/c/,那么上传后你的文件位置是:publicPath/a/b/c/your-file.jsonComplete: 可选项,当 OSS 将所有需要上传的文件上传完成后,会被调用,该方法参数为 complication 对象,里面包含当前打包的信息,你可以合理使用它项目地址github 地址:https://github.com/Vibing/web... ...

June 8, 2020 · 1 min · jiezi

create-react-app-env-环境变量设置

以前搭建脚手架常用cross-env进行环境变量设置。"scripts": { "start": "cross-env REACT_APP_ENV=development node scripts/start.js", "build-dev": "cross-env REACT_APP_ENV=test PUBLIC_URL=/webapps/ai-crm-web node scripts/build.js", "build-uat": "cross-env REACT_APP_ENV=uat PUBLIC_URL=/webapps/ai-crm-web node scripts/build.js", "build": "cross-env REACT_APP_ENV=production PUBLIC_URL=/webapps/ai-crm-web node scripts/build.js", "test": "node scripts/test.js --env=jsdom" },没次都要把相关环境变量写到scripts里面感觉特别冗长。 env.js function env() { if (process.env.REACT_APP_ENV === 'development') { // 本地开发环境 return { ENV: 'development', GETEWAY_BASE: 'https:..', APIROOT: 'https:..', APIVERSION: 'v1.0.0', SSO: { redirect_url: 'https://...', client_id: '', client_secret: '', authorization: '' } } }else if (process.env.REACT_APP_ENV === 'test') { // 线上测试环境 }else{ // 生产环境 }}export default env()虽然有独立的配置文件处理更多环境参数。但是还是希望让代码更简介优雅一些。 ...

June 8, 2020 · 1 min · jiezi

一个webpack入门的大坑

学习webpack打包css的时候,遇到了这个错误: ERROR in ./src/index.css 1:0Module parse failed: Unexpected token (1:0)You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders> .hello{| color: red;| } @ ./src/index.js 1:0-21明明没有任何的错误,却就是报错了,loader的顺序也没错。最后发现,是没有加载config。 解答把package.json的build改为 "build": "webpack --config webpack.config.js",

June 6, 2020 · 1 min · jiezi

webpack-如何同时输出压缩和未压缩的文件

有的时候我们想要同时生成压缩和未压缩的文件,比如我们构建 lib 包的时候,我们希望用户能够使用压缩过后的代码文件作为 cdn 文件,最简单的一个方式就是通过指定环境变量,比如指定 MINIFY,如下: const path = require('path')const isMinify = process.env.MINIFY/** * @type {import('webpack').Configuration} */const config = { entry: { index: './src/index.js' }, output: { filename: isMinify ? '[name].min.js' : '[name].js', path: path.join(__dirname, 'dist') }, devtool: 'cheap-source-map', optimization: { minimize: isMinify ? true : false }}module.exports = config我们在使用的时候通过指定环境变量就可以打包成不同的版本: "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build:min": "MINIFY=1 webpack --config=webpack.config.js", "build": "webpack --config=webpack.config.js" }不过这样的缺点就是我们需要运行两次。 ...

June 5, 2020 · 1 min · jiezi

webpack笔记webpack初识与构建工具发展一

为什么需要构建工具?转换 ES6 语法转换 JSXCSS 前缀补全/预处理器压缩混淆图片压缩前端构建演变之路ant + YUI Toolgruntgulp、fis3webpack、rollup、parcel为什么选择 webpack?社区态丰富配置灵活和插件化扩展官方更新迭代速度快初识webpack,简单的例子入手安装先确保你已经安装了 Node其中检查node是否安装成功,使用node -v;检查npm的版本使用npm -v。创建一个新的项目来开始我们的 webpack 之旅: mkdir webpack-customcd webpack-custom使用 npm init (或者使用npm init-y表示默认配置都选择yes)来创建一个「package.json」文件用于管理项目版本和依赖,然后我们使用 npm安装webpack: npm install webpack webpack-cli -D webpack-cli 是使用 webpack 的命令行工具,在 4.x 版本之后不再作为 webpack 的依赖了,我们使用时需要单独安装这个工具。检查是否安装成功: mac系统:./node_modules/.bin/webpack -v window系统:.node_modules.binwebpack -v 安装成功之后,我们可以在项目的「package.json」文件中看到对应的 webpack 版本依赖: "devDependencies": { "webpack": "^4.43.0", "webpack-cli": "^3.3.11" }构建安装完成之后,我们可以使用 ==npx webpack ==命令来运行项目内安装的 webpack。 其中,我们可以使用如下命令执行一些操作: npx webpack --help:查看 webpack-cli 提供了哪些命令可用npx webpack --version:查看我们安装的版本npx webpack:运行构建我们先添加两个简单的代码文件,「src/index.js」和「src/hello.js」,如代码: index.js代码: import { hello, log } from './hello.js';log(hello);hello.js代码: ...

June 3, 2020 · 1 min · jiezi

VUE引入fontawesome-打包报错-Module-parse-failed-Unexpected-character

将|ttf|eot|woff|woff2加入webpack file-loader配置中

June 2, 2020 · 1 min · jiezi

通过webpack构建library库

构建 librarywebpack 除了打包应用程序代码,还可以用于打包 JavaScript library 应用场景提炼高可服用工具类 code构建 UI 库根据业务需求,在其他 UI 库的基础上做业务组件封装... 兼容性用户应该能够通过以下方式访问 library ES2015 模块import webpackNumbers from 'webpack-numbers' CommonJS 模块require('webpack-numbers') AMDCMD全局变量,当通过 script 脚本引入时Nodejsoutput跟 webpack 打包部署代码最大的不同点就在于 output 输出属性的配置上,webpack 为构建 lib 提供了 library,libraryTarget, libraryExport 等属性 output: { path: path.resolve(__dirname, 'dist'), filename: 'myLib.js', library: 'myLib', // 暴露出去的变量的名字 libraryTarget: 'umd'}library暴露的模块名称。取决于 libraryTarget 的值,当 libraryTarget:commonjs2 时无效 libraryTarget暴露library的方式 变量,以 script 方式引用, 默认值。 node 环境不支持 varassign这两个属性,都是在全局创建一个变量,只有定义与未定义的区别,并不能在 nodejs 中得的支持,并且存在变量冲突的可能性 module.exports = { mode: "production", entry: "./q-library/index.js", output: { path: path.resolve(process.cwd(), "./lib"), filename: "q-library.js", library: "qlibrary", // 模块名称 libraryTarget: "assign", },};输出代码示例 ...

May 30, 2020 · 2 min · jiezi

Webpack如何配置热更新

什么是HMR是指 Hot Module Replacement,缩写为 HMR。对于你需要更新的模块,进行一个"热"替换,所谓的热替换是指在不需要刷新页面的情况下,对某个改动进行无缝更新。如果你没有配置HMR,那么你每次改动,都需要刷新页面,才能看到改动之后的结果,对于调试来说,非常麻烦,而且效率不高,最关键的是,你在界面上修改的数据,随着刷新页面会丢失,而如果有类似Webpack热更新的机制存在,那么,则是修改了代码,不会导致刷新,而是保留现有的数据状态,只将模块进行更新替换。也就是说,既保留了现有的数据状态,又能看到代码修改后的变化。 总结: 加载页面时保存应用程序状态只更新改变的内容,节省调试时间修改样式更快,几乎等同于在浏览器中更改样式依赖package.json: "dependencies": { "css-loader": "^3.2.0", "style-loader": "^1.2.1", "webpack": "^4.41.2", "webpack-dev-server": "^3.10.1"},配置webpack: devServer: { contentBase: path.resolve(__dirname, 'dist'), hot: true, historyApiFallback: true, compress: true},hot为true,代表开启热更新contentBase表示告诉服务器从哪里提供内容。(也就是服务器启动的根目录,默认为当前执行目录,一般不需要设置)historyApiFallback使用HTML5历史记录API时,index.html很可能必须提供该页面来代替任何404响应compress对所有服务启用gzip压缩plugins: { HotModuleReplacementPlugin: new webpack.HotModuleReplacementPlugin()},配置热更新插件 module: { rules: [ { test: /\.(css|less)$/, use: [ process.env.NODE_ENV == 'development' ? { loader: 'style-loader' } : MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 1 } } ] } ]},style-loader库实现了HMR接口,当通过HMR收到更新时,它将用新样式替换旧样式。区分开发环境和生产环境,用不同loader。 src/index.jsx: if (module.hot) { module.hot.accept();}入口文件,新增上面代码,就可以了,非常简单。 ...

May 29, 2020 · 1 min · jiezi

图解Webpack实现Plugin

关注公众号“执鸢者”,回复“书籍”获取大量前端学习资料,回复“前端视频”获取大量前端教学视频。面试工作加分项!Plugin是webpack生态系统的重要组成部分,其目的是解决loader无法实现的其他事,可用于执行范围更广的任务,为webpack带来很大的灵活性。目前存在的plugin并不能完全满足所有的开发需求,所以定制化符合自己需求的plugin成为学习webpack的必经之路。下面将逐步阐述plugin开发中几个关键技术点并实现plugin。 一、Webpack构建流程在实现自己的Plugin之前,需要了解一下Webpack的构建流程(下图)。Webpack的构建流程类似于一条生产线(webpack的事件流机制),在特定时机会广播对应的事件,插件就可以监听这些事件的发生,从而在特定的时机做特定的事情。 上图中展示了Webpack构建流程的三个阶段及每个阶段广播出来的事件。初始化阶段:启动构建;紧接着从配置文件和Shell语句中读取并合并参数,得到最终参数;用上一步得到的参数初始化Compiler对象并加载所有配置的插件。从Entey出发,针对每个Module串行调用对应的Loader去翻译文件的内容,再找到该Module依赖的Module,递归地进行编译处理,得到每个模块被翻译后的最终内容及它们之间的依赖关系。根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再将每个Chunk转换成一个单独的文件加入到输出列表中,最终将所需输出文件的内容写入文件系统中。二、编写Plugin编写Plugin主要分为四个步骤: 创建一个具名JavaScript函数在其原型上定义apply方法给webpack构建流程中相关事件挂载事件钩子进行监听钩子函数触发后,利用compiler、compilation等进行相关操作,达到需要的效果2.1 基本结构class MyPlugin { constructor(options) { } apply(compiler) { console.log(compiler); compiler.hooks.emit.tap('MyPlugin', () => { console.log('plugin is used!!!'); }) }}module.exports = MyPlugin;2.2 apply方法apply 方法在安装插件时,会被 webpack compiler 调用一次。apply 方法可以接收一个 webpack compiler 对象的引用,从而可以在回调函数中访问到 compiler 对象。2.3 钩子函数在webpack整个编译过程中暴露出来大量的Hook供内部/外部插件使用,将Hook注册后即可实现对整个webpack事件流中对应事件的监听。这些钩子函数的核心是Tapable,该包暴露出很多钩子类,利用这些类创建了上述钩子函数。常见的钩子主要有以下几种: 暴露在compiler和compilation上的钩子函数均是基于上述钩子构建。 2.4 钩子函数注册方式 在plugin中,钩子注册方式有三种:tap、tapAsync、tapPromise,根据钩子函数的类型采用相应的方法进行注册。同步钩子函数利用tap注册,异步钩子函数利用tap、tapAsync、tapPromise进行注册。taptap可以用来注册同步钩子也能用来注册异步钩子。 compiler.hooks.compile.tap('MyPlugin', compilationParams => { console.log('以同步方式触及compile钩子')});tapAsynctapAsync能够用来注册异步钩子,并通过callback告知Webpack异步逻辑执行完毕。 compiler.hooks.run.tapAsync('MyPlugin', (compiler, callback) => { console.log('tapAsync 异步'); callback();});tapPromisetapPromise能够用来注册异步钩子,其通过返回Promise来告知Webpack异步逻辑执行完毕。(实现方式有两种,一种是通过返回Promse函数,另一种是利用async实现)。 // 方式一compiler.hooks.run.tapPromise('MyPlugin', (compiler) => { return new Promise(resolve => setTimeout(resolve, 1000)).then(() => { console.log('tapPromise 异步') })})// 方式二compiler.hooks.run.tapPromise('MyPlugin', async (compiler) => { await new Promise(resolve => setTimeout(resolve, 1000)); console.log('tapPromise 异步 async')})2.5 Compiler和CompilationCompiler和Compilation是Plugin和Webpack之间的桥梁,所以了解其具体含义至关重要,其含义如下:Compiler 对象包含了 Webpack 环境的所有配置信息,包含options、loaders、plugins等信息。这个对象在 Webpack 启动时被实例化,它是全局唯一的,可以简单地将它理解为 Webpack 实例。Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack以开发模式运行时,每当检测到一个文件发生变化,便有一次新的 Compilation 被创建。Compilation对象也提供了很多事件回调供插件进行扩展。通过 Compilation也能读取到 Compiler 对象。 ...

May 26, 2020 · 2 min · jiezi

Egg-React-SSR-服务端渲染-Webpack-构建流程

1. 本地Egg项目启动 首先执行node index.js 或者 npm run dev 启动 Egg应用在 Egg Agent 里面启动koa服务, 同时在koa服务里面启动Webpack编译服务挂载Webpack内存文件读取方法覆盖本地文件读取的逻辑app.react.render = (name, locals, options) => { const filePath = path.isAbsolute(name) ? name : path.join(app.config.view.root[0], name); const promise = app.webpack.fileSystem.readWebpackMemoryFile(filePath, name); return co(function* () { const code = yield promise; if (!code) { throw new Error(`read webpack memory file[${filePath}] content is empty, please check if the file exists`); } // dynamic execute javascript const wrapper = NativeModule.wrap(code); vm.runInThisContext(wrapper)(exports, require, module, __filename, __dirname); const reactClass = module.exports; if (options && options.markup) { return Promise.resolve(app.react.renderToStaticMarkup(reactClass, locals)); } return Promise.resolve(app.react.renderToString(reactClass, locals)); });};Worker 监听Webpack编译状态, 检测Webpack 编译是否完成, 如果未完成, 显示Webpack 编译Loading, 如果编译完成, 自动打开浏览器Webpack编译完成, Agent 发送消息给Worker,  Worker检测到编译完成, 自动打开浏览器, Egg服务正式可用 ...

November 3, 2019 · 1 min · jiezi

vue-webpack-获取文件夹下的所有vue文件

const path = require('path')const files = require.context('@com/hlfeature/allFeaturePages', true, /.vue$/)const modules = {};files.keys().forEach(item => { const name = path.basename(item,'.vue') modules[name] = files(item).default || files(item)})

November 2, 2019 · 1 min · jiezi

前端技术之常用webpack插件

1、html-webpack-pluginSimplifies creation of HTML files to serve your webpack bundles.主页地址:https://github.com/jantimon/h...安装方法:npm i --save-dev html-webpack-plugin 2、extract-text-webpack-pluginExtracts text from a bundle into a separate file.主页地址:https://github.com/webpack-co...安装方法:npm install --save-dev extract-text-webpack-plugin 3、style-loaderAdds CSS to the DOM by injecting a <style> tag.主页地址:https://github.com/webpack-co...安装方法:npm install style-loader --save-dev 4、css-loaderThe css-loader interprets @import and url() like import/require() and will resolve them.主页地址:https://github.com/webpack-co...安装方法:npm install --save-dev css-loader 5、sass-loaderLoads a Sass/SCSS file and compiles it to CSS.主页地址:https://github.com/webpack-co...安装方法:npm install sass-loader node-sass --save-dev ...

October 16, 2019 · 1 min · jiezi

第六篇-仿写Vue生态系列模板loader与计算属性

( 第六篇 )仿写'Vue生态'系列___"模板loader与计算属性"本次任务 编写'cc-loader', 使我们可以使用'xxx.cc'文件来运行我的框架代码.为'cc_vue'添加生命周期函数.新增'计算属性'.新增'观察属性'.一.'cc-loader'的定义与编写本次只是编写一个最基础的版本, 后续完善组件化功能的时候, 会对它有所改动.使'webpack'可以解析后缀为'cc'的文件.必须做到非常的轻量.让我们一步一步来做出这样一个'loader', 首先我先介绍一下文件结构, 在'src'文件夹平级建立'loader'文件夹, 里面可以存放以后我做的所有'loader'. 不可或缺的步骤就是定义'loader'的路径.cc_vue/config/common.js新增resolveLoader项 resolveLoader: { // 方式1: // 如果我书写 require('ccloader'); // 那么就会去 path.resolve(__dirname, '../loader')寻找这个引入. alias: { ccloader: path.resolve(__dirname, '../loader') }, // 方式2: 当存在require('xxx');这种写法时, 先去'node_modules'找寻, 找不到再去path.resolve(__dirname,'../loader')找找看. modules:[ 'node_modules', path.resolve(__dirname,'../loader') ] },'loader'文件的配置写完了, 那么可以开始正式写这个'loader'了.cc_vue/loader/cc-loader.js // 1: 'source'就是你读取到的文件的代码, 他是'string'类型.function loader(source) { // ..具体处理函数 // 2: 处理完了一定要把它返回出去, 因为可能还有其他'loader'要处理它, 或者是直接执行处理好的代码. return source;}module.exports = loader;模板的定义 本次暂不处理title, 因为没啥技术含量暂时也没必要.'#--style-->' 会被替换为css的内容.'#--template-->' 会被替换为模板的内容.<!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> #--style--> </head> <body> <div id="app"> #--template--> </div> </body></html>最终要达到的'.cc'文件的书写方式 ...

October 15, 2019 · 3 min · jiezi

styleloader源码解析

首先打开style-loader的package.json,找到main,可以看到它的入口文件即为:dist/index.js,内容如下:` var _path = _interopRequireDefault(require("path"));var _loaderUtils = _interopRequireDefault(require("loader-utils"));var _schemaUtils = _interopRequireDefault(require("schema-utils"));var _options = _interopRequireDefault(require("./options.json"));function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }module.exports = () => {};module.exports.pitch = function loader(request) { // ...}`其中_interopRequireDefault的作用是:如果引入的是 es6 模块,直接返回,如果是 commonjs 模块,则将引入的内容放在一个对象的 default 属性上,然后返回这个对象。我首先来看pitch函数,它的内容如下:` // 获取webpack配置的optionsconst options = _loaderUtils.default.getOptions(this) || {};// (0, func)(),运用逗号操作符,将func的this指向了windows,详情请查看:https://www.jianshu.com/p/cd188bda72df// 调用_schemaUtils是为了校验options,知道其作用就行,这里就不讨论了(0, _schemaUtils.default)(_options.default, options, { name: 'Style Loader', baseDataPath: 'options'});// 定义了两个变量,**insert**、**injectType**,不难看出insert的默认值为head,injectType默认值为styleTagconst insert = typeof options.insert === 'undefined' ? '"head"' : typeof options.insert === 'string' ? JSON.stringify(options.insert) : options.insert.toString();const injectType = options.injectType || 'styleTag';switch(injectType){ case 'linkTag': { // ... } case 'lazyStyleTag': case 'lazySingletonStyleTag': { // ... } case 'styleTag': case 'singletonStyleTag': default: { // ... }}`在这里,我们就看默认的就好了,即insert=head,injectType=styleTag` ...

October 15, 2019 · 4 min · jiezi

前端技术之webpack热模块替换HMR

第一步:安装HMR中间件:npm install --save-dev webpack-hot-middleware第二步:webpack配置中引入webpack对象 const webpack = require('webpack’);第三步:增加devServer配置项: hot: true第四步:增加热模块替换插件: new webpack.HotModuleReplacementPlugin()注意:1、ExtractTextPlugin插件可能会导致HMR无效。另外,还可以通过使用web-dev-server —hotOnly参数来启用HMR。参考官网链接:https://webpack.js.org/guides...https://github.com/webpack-co...React、Vue、Angular相关技术,请参考以下内容: React Hot Loader: Tweak react components in real time.Vue Loader: This loader supports HMR for vue components out of the box.Elm Hot Loader: Supports HMR for the Elm programming language.Angular HMR: No loader necessary! A simple change to your main NgModule file is all that's required to have full control over the HMR APIs.

October 15, 2019 · 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

优化-web-应用程序性能方案总结

在开发 web 应用程序时候,性能都是必不可少的话题。而大部分的前端优化机制都已经被集成到前端打包工具 webpack 中去了,当然,事实上仍旧会有一些有趣的机制可以帮助 web 应用进行性能提升,在这里我们来聊一聊能够优化 web 应用程序的一些机制,同时也谈一谈这些机制背后的原理。 Chrome Corverage 分析代码覆盖率在讲解这些机制前,先来谈一个 Chrome 工具 Corverage。该工具可以帮助查找在当前页面使用或者未使用的 JavaScript 和 CSS 代码。 工具的打开流程为: 打开浏览器控制台 consolectrl+shift+p 打开命令窗口在命令窗口输入 show Coverage 显示选项卡 webpackjs 其中如果想要查询页面加载时候使用的代码,请点击 reload button如果您想查看与页面交互后使用的代码,请点击record buton这里以淘宝网为例子,介绍一下如何使用 上面两张分别为 reload 与 record 点击后的分析。 其中从左到右分别为 所需要的资源 URL资源中包含的 js 与 css总资源大小当前未使用的资源大小左下角有一份总述。说明在当前页面加载的资源大小以及没有使用的百分比。可以看到淘宝网对于首页代码的未使用率仅仅只有 36%。 介绍该功能的目的并不是要求各位重构代码库以便于每个页面仅仅只包含所需的 js 与 css。这个是难以做到的甚至是不可能的。但是这种指标可以提升我们对当前项目的认知以便于性能提升。 提升代码覆盖率的收益是所有性能优化机制中最高的,这意味着可以加载更少的代码,执行更少的代码,消耗更少的资源,缓存更少的资源。 webpack externals 获取外部 CDN 资源一般来说,我们基本上都会使用 Vue,React 以及相对应的组件库来搭建 SPA 单页面项目。但是在构建时候,把这些框架代码直接打包到项目中,并非是一个十分明智的选择。 我们可以直接在项目的 index.html 中添加如下代码 <script src="//cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.runtime.min.js" crossorigin="anonymous"></script> <script src="//https://cdn.jsdelivr.net/npm/vue-router@3.1.3/dist/vue-router.min.js" crossorigin="anonymous"></script>然后可以在 webpack.config.js 中这样配置 ...

October 13, 2019 · 4 min · jiezi

webpack-loader-从上手到理解系列vueloader一

原文地址 1 什么是 vue-loadervue-loader 是一个 webpack 的 loader,它允许你以一种名为单文件组件的格式撰写 Vue 组件。2 如何使用 vue-loader2.1 安装npm install vue-loader vue-template-compiler --save-dev2.2 配置 webapck// webpack.config.jsconst VueLoaderPlugin = require('vue-loader/lib/plugin')module.exports = { mode: 'development', module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' }, // 它会应用到普通的 `.js` 文件 // 以及 `.vue` 文件中的 `<script>` 块 { test: /\.js$/, loader: 'babel-loader' }, // 它会应用到普通的 `.css` 文件 // 以及 `.vue` 文件中的 `<style>` 块 { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ] } ] }, plugins: [ // 请确保引入这个插件来施展魔法 new VueLoaderPlugin() ]}2.3 创建一个 Vue 组件一个标准的 Vue 组件可以分为三部分: ...

October 9, 2019 · 3 min · jiezi

深入浅出webpack有感

对于前端仔来说,相信大家对webpack都再熟悉不过了,但是你对webpack的了解程度又有多深呢,笔者花了几天时间看了一下《深入浅出webpack》,虽然说书中大部分介绍的是配置和使用相关的,但是如果你对webpack的配置、使用、原理和构建流程更加熟悉的话,对于你的开发可以说是百里无一害!本文不会局限于介绍配置,也不会详细介绍打包原理(后面打算写一篇有关webpack打包原理的~),更多着重于webpack打包的思想介绍。没有打包构建的日子nodejs的出现对于构建工具具有重要的意义,在没有nodejs之前,js只能执行在浏览器环境下,所以意味着对发布前的js文件要进行处理,十分局限,没有打包工具,只能用PHP脚本来处理文件,甚至还需要借助一些在线压缩网站,开发体验十分差劲,在史前时代存在以下几个痛点:1、缺乏文件处理工具,对文件进行编译或其他预处理,进行打包压缩等工作;2、缺乏文件的模块化,引入第三方库直接用cdn引入,需要处理依赖管理,人为控制脚本的加载顺序,并且存在全局变量命名冲突的问题;3、缺乏代码校验和自动化测试,在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。 有了打包构建工具的日子随着nodejs的诞生,我们可以在开发环境下书写nodejs代码脚本,对我们的前端代码做预处理,编译压缩等工作,最初诞生的是grunt和gulp,Grunt和Gulp都属于任务流工具Tast Runner,两者都是通过配置好配置文件,但是相比之下,gulp通过函数式编写配置文件,以及前端人员所熟悉的链式调用,让大家觉得更易懂更易上手,gulp本身借鉴了grunt的经验进行升级和加入一些新特性。正因为流管理多任务配置输出方式的提高,人们逐渐选择使用Gulp而放弃grunt。有了grunt和gulp,文件压缩处理的工作解决了,代码校验和测试也可以处理了,但是模块化仍没有结果?其实前端的痛点还远不止模块化那么简单,频繁的DOM节点处理,JS里杂糅了交互逻辑、请求逻辑、数据处理和校验逻辑、DOM操作逻辑,导致JQ书写的代码就更意大利炒大便,呸!意大利炒面一样。在团队开发中,可能你的代码要给别人维护,这就非常痛苦了。 webpack诞生记1、模块化思想隔离不同的js文件,模块化开发,仅暴露当前模块所需要的其他模块,这是模块化思想想要传递给我们的。nodejs诞生后,后端所采取的模块化思想是commonjs,然而,不同于后端,前端的代码运行在浏览器端,有两点不同之处:1、没有nodejs执行环境,不支持module.exports的书写格式;2、后端require一个文件,是读取本地文件的形式,速度极快,而对于前端而言,需要去动态加载一个js文件,存在额外耗时。于是AMD思想应运而生,对此的相应实现是requireJS,允许你定义好模块名称、模块依赖以及当前的模块代码(function),通过广度优先遍历的方式,递归加载父模块所依赖的子模块,但是这也暴露出了一些问题:1、通过js加载执行后再去加载其依赖的子模块,这个递归加载过程本身是耗时的;2、模块化思想提倡我们分隔逻辑,管理好各个js文件内的逻辑,一旦分割的JS文件过多,最终造成前端资源加载压力。不过不用担心,requireJS提供了r.js来处理发布前的模块合成,帮助你把多个JS文件打包成一个文件。再到后来,国内出现了CMD的思想,不同于AMD的声明依赖的形式,允许你动态加载依赖,但是其实现以及具体的运行结果被大家诟病。再到后后来,为了解决这种不同库的模块实现不一致的问题,提出了UMD,其实很简单,只是写一段hack,让你的模块能够兼容不同的模块加载场景,无论是commonjs还是AMD,如果都没有的话就直接声明为一个全局变量的形式。下面引用一段UMD的代码: (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = global || self, global.Vue = factory());}(this, function () { 'use strict'; //your code here}再到后后后来,未来的大一统,es6中所提出的import和export的形式来统一前后端的模块化加载方式。相比以往的实现,import/export的形式,在模块化加载JS文件的时候,保留动态的执行引用,其次,不允许动态控制加载依赖,使得tree-shaking成为可能。 2、组件化思想组件化的思想并非前端所特有,在客户端也会面临相同的问题。想象一下,A跟B被同时分配一起开发完成一个首页页面,包括导航栏、轮播图、网站列表数据,登录框等,两人需要如何分工协作?导航栏、列表这种需要在多个页面复用的HTML怎么办?假如在没有组件化处理的情况下:1、A和B分工困难麻烦,代码提交时会处理大量的冲突;2、导航栏、列表等多处复用的地方,需要cv大法直接复制粘贴到另一个页面中去使用。组件化思想,让我们把页面划分为一个个组件,组件内部维护自己的UI展示、交互逻辑,组件间可以进行数据通信,实现一种变相的相互隔离,便不会出现A和B两人一起编辑一段html的难受场景,同时,提高了代码的可维护性和复用性,这是其解决的关键痛点。引用vue官网的一张有关组件化思想的图: 3、MVC框架、MVVM框架的流行在模块化和组件化的基础上,实现了页面组件之间的隔离和各自维护,但是面临的最大的一个痛点问题,仍然是前面所说的,前端的JS逻辑中杂糅了各种处理逻辑,交互逻辑、请求逻辑、数据处理和校验逻辑、DOM操作逻辑;而其实这一切可以划分为两个层次,一个是数据层,一个是视图层,如何避免重复的书写操作DOM的逻辑,如果说早期的各种模板引擎给了我们初期的解决方案,那么vue、react以及angular就是在模板引擎的基础上的上层建筑。为了让我们更加专注于数据的处理,MVC框架和MVVM框架帮我们做了以下两件事:1、监听页面操作事件,触发相应的事件钩子,执行代码逻辑,即V层到M层的过程;2、执行代码逻辑后,数据层发生修改,帮我们更新渲染页面,即M层到V层的过程;vue中通过vm实现,react中通过触发setState通知。如此,我们只需要书写一次html,在html中写明绑定或展示的数据,同时绑定好事件监听器,后续便不需要再处理视图层相关的操作,只需要关注于自己的业务逻辑代码、数据层的处理等。MVVM架构流程图: 4、代码打包构建前面介绍了grunt、gulp打包构建工具,其实webpack本质也是打包构建工具,但是webpack呈现出来的功能更为强大和成熟。对于代码的预处理、模块化加载、代码分割等,webpack具有更大的优势。 webpack诞生! 读者读到这里,可能仍有些许疑惑,前面讲了这么多,为啥还是没有介绍到webpack相关的,其实不然,仔细回想一下前面所介绍的思想,以及你平时使用webpack来打包构建的时候,其实webpack正是帮你处理了这些繁杂琐碎的事情。如果代码预处理压缩足以,那么grunt和gulp已经满足了;如果说模块化开发足以,那么requireJS和Browserify已经满足了;如果说组件化开发、MV*框架足以,那么只需要在页面内引入相应的vue或react框架,足矣。笔者写这边文章,更多是想让大家能够思考工具或者框架背后,所呈现出来的思想,webpack就像是一个巨无霸,集大成者,它解决了打包构建,它处理了模块化开发,它帮助你和其他框架完美融合实现组件化开发;而这几年来MV*框架的流行对于webpack市场的迅速扩展有着不小的贡献。 webpack为我们做了以下这些事:(引自《深入浅出webpack》)代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等。 文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。 模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。 自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。 代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。 ...

October 8, 2019 · 1 min · jiezi

webpack-系列10-分钟搞定-styleloader

原文地址 前言webpack loaders 系列文章: 10 分钟搞定 file-loader10 分钟搞定 url-loader搞定 style-loader什么是 style-loaderstyle-loader 的功能就一个,在 DOM 里插入一个 <style> 标签,并且将 CSS 写入这个标签内。 简单来说就是这样: const style = document.createElement('style'); // 新建一个 style 标签style.type = 'text/css';style.appendChild(document.createTextNode(content)) // CSS 写入 style 标签document.head.appendChild(style); // style 标签插入 head 中稍后会详细分析源码,看看和我们的思路是否一致。 如何使用 style-loader1. 安装 style-loadernpm install style-loader --save-dev2. 配置 webapck// webpack.config.jsmodule.exports = { module: { rules: [ { test: /\.(css)$/, use: [ { loader: 'style-loader', options: {}, }, { loader: 'css-loader' }, ], }, ], },};日常的开发中处理样式文件时,一般会使用到 style-loader 和 css-loader 这两个 loader。 ...

October 8, 2019 · 3 min · jiezi

webpack常用加载器插件总结看这一篇就够了

前言webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。webpack学习的一个比较难的点便是它的配置与配置的组合,本文总结了webpack中的常用,主流的配置,并简单介绍了webpack在项目中的应用。可以帮助初学者快速的定位所需要的功能,当然每个加载器(loader)插件(plugins)的功能详解用法限于篇幅不能展示,建议大家去npm官网查看其最新配置。因为这方面内容较多,本文会不断迭代。大家可以收藏加关注,并在评论中指出优化的地方。非常感谢注意:本文基于webpack4 npm官网地址:https://www.npmjs.com/babel:https://www.babeljs.cn/1.常用loader及功能babel-loader @babel/code @babel/preset-env babel-polyfill(严格上是个插件)js语法转换工具,可以将ES6或更高级的语法转换成浏览器支持的语法 css-loader处理入口文件中的css style-loader处理文件模块中的style样式 postcss-loader第一个就是前面提到的把 CSS 解析成 JavaScript 可以操作的 抽象语法树结构(Abstract Syntax Tree,AST),第二个就是调用插件来处理 AST 并得到结果。 file-loader处理引入的静态文件,可以按目录分类 url-loader同file-loader,处理引入的静态文件,还可以把小文件转换为base64格式的URL,从而减少网络请求次数。url-loader依赖file-loader。 eslint-loader语法检测loader,会在打包或编译的时候提示语法问题 2.常用插件及功能autoprefixer一款自动管理浏览器前缀的插件,它可以解析CSS文件并且添加浏览器前缀到CSS内容里,使用Can I Use(caniuse网站)的数据来决定哪些前缀是需要的。 html-webpack-plugin当使用 webpack 打包时,创建一个 html 文件,并把 webpack 打包后的静态文件自动插入到这个 html 文件当中。 mini-css-extract-plugin将CSS提取为独立的文件的插件,对每个包含css的js文件都会创建一个CSS文件,支持按需加载css和sourceMap optimize-css-assets-webpack-plugin压缩优化css uglifyjs-webpack-plugin压缩优化js webpack-dev-server启动一个本地临时的服务器,可以设置端口,支持热更新(将HotModuleReplacementPlugin插入到进程中), CleanWebpackPlugin能帮忙每次打包之前先删除dist文件夹。 copyWebpackPlugin可以把其他的文件或文件夹拷贝到打包文件里 bannerPlugin(webpack)版权字符串 ignorePlugin忽略掉引入的包 webpack-merge 可以抽取出 开发与生产环境的相同的webapck配置。vue里的配置文件dev.config.js, prod.config.js就基于这个插件 html-withimg-plugin处理html中的img definePlugin定义环境变量 dllPlugin 在一个额外的独立的 webpack 设置中创建一个只有 dll 的 bundle(dll-only-bundle)。 这个插件会生成一个名为 manifest.json 的文件,这个文件是用来让 DLLReferencePlugin 映射到相关的依赖上去的。 ...

October 7, 2019 · 1 min · jiezi

解决webapck多页面内存溢出

因为自己的项目是基于vue-cli3进行开发,所以这里只讨论这种情况下的解决办法 在进行多页面开发的时候,项目刚开始阶段,因为文件较少,所以代码编译速度还行,但是随着项目逐渐增大,webpack编译的速度越来越慢,并且经常出现内存溢出的情况。下面就是几种尝试的方法,加快编译的速度 增加Node运行内存在Node中通过JavaScript使用内存时只能使用部分内存(64位系统下约为1.4 GB,32位系统下约为0.7 GB)。所以不管电脑实际的运行内存是多少,Node在运行代码编译的时候,使用内存大小不会发生变化。这样就可能导致因为原有的内存不够,导致内存溢出。所以可以增加Node的运行内存,下面是两种方法 更改cmd在node_modules/.bin/vue-cli-server.cmd把下面代码复制上去 @IF EXIST "%~dp0\node.exe" ( "%~dp0\node.exe" --max_old_space_size=4096 "%~dp0\..\@vue\cli-service\bin\vue-cli-service.js" %*) ELSE ( @SETLOCAL @SET PATHEXT=%PATHEXT:;.JS;=;% node --max_old_space_size=4096 "%~dp0\..\@vue\cli-service\bin\vue-cli-service.js" %*)更改package.json把启动Node服务的更改下: node --max_old_space_size=4096 node_modules/@vue/cli-service/bin/vue-cli-service.js serve本质上没啥区别,都是通过强行增加Node的运行内存,来解决内存溢出的问题。但是这种方法不治本,虽然不会造成内存溢出,但是编译速度还是挺慢的,编译完成还是需要等很久。 配置需要编译的文件这种就是按需配置需要编译的文件,为什么出现内存溢出,本质上还是因为需要编译的文件太多了,那我们就可以减少需要编译的页面就可以解决这个问题。 首先所有的页面配置都是放在page.config.js,如果我们需要对某些特定页面进行配置,就需要过滤所有的页面,获取需要编译的页面,下面是编译文件的写法 const path = require('path');const fs = require('fs');const pages = require('../pages.config');const params = JSON.parse(process.env.npm_config_argv).original;const buildPath = params[params.length - 1].match(/[a-zA-Z0-9]+/)[0] || '';let buildConfig = { pages: [],};if (!/(test|online|serve)/gi.test(buildPath)) { const configJsPath = path.resolve(__dirname, `${buildPath}.js`); // 如果该路径存在 if (fs.existsSync(configJsPath)) { // eslint-disable-next-line import/no-dynamic-require buildConfig = require(configJsPath); } else if (pages[buildPath]) { buildConfig.pages = buildPath.split(','); } else { throw new Error('该路径不存在'); }} else { buildConfig = require('./default');}module.exports = buildConfig.pages;大多数情况下,一个产品都是由多个业务线构成,每次可能需要更改的就是某一条业务线,就完全可以单独创建一个这条业务线的配置文件,然后再这个文件写入你需要编译的页面名称,就可以单独编译这个页面,或者说在调用通过传入的字符串来编译那些页面 ...

October 7, 2019 · 2 min · jiezi

重磅从0实现一个axios网络请求框架百分之七十的功能

我们知道,axios是前端一个非常优秀的对于网络请求的框架,其特点主要是请求方便、功能多(如拦截器)。那么作为一枚前端开发人员,了解并能够使用axios其实是基础,深入了解其实现原理才是比较重要的,当然,如果能徒手撸一个axios类似的框架出来,那就是相当的不错了。这篇文章会从以下几个大的点来实现一个axios框架: axios的本质是什么?axios默认值、参数的实现常见请求方式:get、post、delete在axios中的实现真正请求(XMLHttpRequest)的实现axios拦截器的实现打包发布同时希望你了解以下的一些知识点: webpackaxios框架的基本使用Es6的Proxy、Class等XMLHttpRequesthttp协议...等axios的本质是什么使用axios框架的时候,我们大部分情况都是以模块的形式引入进行使用,如: import axios from 'axios'发出一个get请求,如: axios.get('/user')或axios('/user')从中可以看出,axios的本质就是一个函数,那么就先来实现一个axios的雏形。(本篇文章实现利用es6的calss去实现)。 axios.js class Axios{ constructor(){ }}export default new Axios();index.js import axios from './axios'这里毫无疑问可以看出,axios现在只是一个Axios的实例,并不是一个函数,那么要怎样将axios变成一个函数?这就需要两个知识点:Axios的构造函数是可以进行返回值操作、利用ES6的Proxy。更进一步的Axios类的代码如下: class Axios{ constructor(){ return new Proxy(function(){},{ apply(fn,thisArg,agrs){ } }) }}export default new Axios();这样我们得到的一个Axios实例axios就是一个函数了。这里简单提一下,Proxy在进行函数处理的使用,apply是很有用的,使用它我们就可以对一个函数进行代理处理了。 看看打印出来的内容: 接下来,要怎样才能够实现axios('/user')或axios.get('/user')这样的效果呢?,我们知道了axios是一个函数,那么给一个函数增加一个属性(也是函数),就能够解决这个问题了。先来简单看下函数式的写法: 继续完善Axios类中的代码: class Axios{ constructor(){ const _this = this; return new Proxy(function(){},{ apply(fn,thisArg,agrs){ }, get(fn,key){ return _this[key]; }, set(fn,key,val){ _this[key] = val; return true; } }) } get(){ console.log('get request'); }}export default new Axios();这样就实现了axios('/user')或axios.get('/user')这样的效果了。 ...

October 6, 2019 · 3 min · jiezi

webpack4-中如何实现资源内联

本篇文章会系统的介绍下 Webpack4 里面资源内联(HTML/CSS/JS/Image/Font)的正确姿势首先,我们一起了解下什么是资源内联。 什么是资源内联?资源内联(inline resource),就是将一个资源以内联的方式嵌入进另一个资源里面,我们通过几个小例子来直观感受一下。 HTML 内联 CSS,这个其实就是我们通常说的 内联 CSS 或者 行内 CSS。我们可以写几行 reset CSS,然后通过 style 标签的方式嵌入进了 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>Document</title> <style> * { margin: 0; padding: 0; } body { font-size: 12px; font-family: Arial, Helvetica, sans-serif; background: #fff; } ul, ol, li { list-style-type: none; } </style></head><body> </body></html>CSS 内联图片,就是我们通常将小图片通过 base64 的方式内嵌进 CSS 里面。我们可以将搜索小 icon 内联进 CSS: // index.css.search { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABJ0lEQVQ4T6XSsUoEMRAG4H/ClZaLmbSW1pZ6+gAnFrK+gZXoK6jvIILgE6gIcnYWgmJno6AgYp1Z2EcIGQnsHbuaQ9abMkO+TGaGMGfQnPfxC3DOrajqPoB1AArgnohOvffPucc6ADMfAjgCUMYYH9MFY8wagEsAxyKScp2YAtbaERGNRST7LWZWVd2squq2LbSBMyK6E5GrXKnW2i1jzMh7v5sFmPkzhDCs69rngKIo3GAweBKRpVnAVwhh9Q/gRUQWs4Bz7jzGeFNV1ThXATOXAA5EJDV1Gr2aSETb3vvrLJAOmTmNKY2yVNUHVSVjzBDABYA3ADsi8j4TSIlmkfYAbABYUNUPACdE9NpAHaTXKjPz8k+kF9B8s4P0BibIpBf/AtpN/AYx54AR58WxmQAAAABJRU5ErkJggg==) no-repeat;}了解了资源内联的基本概念后,可能你会问资源内联有什么意义?接下来我们从几个维度去看看为什么我们需要资源内联。 ...

October 5, 2019 · 3 min · jiezi

炒冷饭系列-10-分钟搞定-urlloader

原文地址 什么是 url-loaderurl-loader 会将引入的文件进行编码,生成 DataURL,相当于把文件翻译成了一串字符串,再把这个字符串打包到 JavaScript。 什么时候使用一般来说,我们会发请求来获取图片或者字体文件。如果图片文件较多时(比如一些 icon),会频繁发送请求来回请求多次,这是没有必要的。此时,我们可以考虑将这些较小的图片放在本地,然后使用 url-loader 将这些图片通过 base64 的方式引入代码中。这样就节省了请求次数,从而提高页面性能。 如何使用1. 安装 url-loadernpm install url-loader --save-dev2. 配置 webapckmodule.exports = { module: { rules: [ { test: /\.(png|jpg|gif)$/, use: [ { loader: 'url-loader', options: {}, }, ], }, ], },};3. 引入一个文件,可以是 import(或 require)import logo from '../assets/image/logo.png';console.log('logo的值: ', logo); // 打印一下看看 logo 是什么简单三步就搞定了。 4. 见证奇迹的时刻webpack执行 webpack 之后,dist 目录只生成了一个 bundle.js。和 file-loader 不同的是,没有生成我们引入的那个图片。上文说过,url-loader 是将图片转换成一个 DataURL,然后打包到 JavaScript 代码中。 那我们就看看 bundle.js 是否有我们需要的 DataURL: ...

September 30, 2019 · 2 min · jiezi

炒冷饭系列-10-分钟搞定-fileloader

原文地址 什么是 file-loader简单来说,file-loader 就是在 JavaScript 代码里 import/require 一个文件时,会将该文件生成到输出目录,并且在 JavaScript 代码里返回该文件的地址。 如何使用1. 安装 file-loadernpm install file-loader --save-dev2. 配置 webapck// webpack.config.jsmodule.exports = { module: { rules: [ { test: /\.(png|jpg|gif)$/, use: [ { loader: 'file-loader', options: {}, }, ], }, ], },};关于 file-loader 的 options,这里就不多说了,见 file-loader options . 3. 引入一个文件,可以是 import(或 require)import logo from '../assets/image/logo.png';console.log('logo的值: ', logo); // 打印一下看看 logo 是什么简单三步就搞定了。 4. 见证奇迹的时刻webpack执行 webpack 打包之后,dist 目录下会生成一个打包好的 bundle.js,这个就不多说了。 如果使用了 file-loader, dist 目录这时候会生成我们用到的那个文件,在这里也就是 logo.png。 ...

September 30, 2019 · 2 min · jiezi

从0搭建自己的webpack开发环境四

往期回顾:从0搭建自己的webpack开发环境(一)从0搭建自己的webpack开发环境(二)从0搭建自己的webpack开发环境(三) 经过三期的学习,本篇文章将介绍TS和React/Vue的结合使用,搭载Webpack,助力成长前端高级技术体系。下面继续一起学习: 1.配置TS环境1.1 使用ts-loader使用typescript需要安装ts相关配置 npm install typescript ts-loader --save-dev生成ts的配置文件 npx tsc --init配置ts-loader { test:/\.tsx?/, use: ['ts-loader'], exclude: /node_modules/}将入口文件更改成ts文件 let a:string = 'hello';console.log(a);执行npm run dev发现已经可以正常的解析ts文件啦! 1.2 使用 preset-typescript不需要借助typescript npm install @babel/preset-typescript{ "presets": [ ["@babel/preset-env",{ "useBuiltIns":"usage", "corejs":2 }], "@babel/preset-react", ["@babel/preset-typescript",{ "allExtensions": true }] ], "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties",{"loose":true}], "@babel/plugin-transform-runtime" ]}2.配置ts+react环境安装react相关模块 npm i @babel/preset-react --save-dev # 解析jsx语法npm i react @types/react @types/react-dom react react-dom typescriptimport React from 'react';import ReactDOM from 'react-dom';const state = {number:0};type State = Readonly<typeof state>;class Counter extends React.Component<object,State>{ state:State = state handleClick =()=>{ this.setState({number:this.state.number+1}) } render(){ const {number} = this.state; return ( <div> <button onClick={this.handleClick}>点击</button> {number} </div> ) }}ReactDOM.render(<Counter></Counter>,document.getElementById('root'));3.配置ts+vue环境安装vue所需要的模块 ...

September 19, 2019 · 2 min · jiezi

一看就懂之webpack原理解析与实现一个简单的webpack

前情回顾一看就懂之webpack基础配置一看就懂之webpack高级配置与优化 一、简介本文主要讲述的是webpack的工作原理,及其打包流程,一步步分析其打包过程,然后模拟实现一个简单的webpack,主要是为了更深刻地了解其打包流程,为了充分体现其山寨的意义,故名称定为web-pack。二、webpack的一些特点webpack的配置文件是一个.js文件,其采用的是node语法,主要是导出一个配置对象,并且其采用commonjs2规范进行导出,即以module.exports={}的方式导出配置对象,之所以采用这种方式是为了方便解析配置文件对象,webpack会找到配置文件然后以require的方式即可读取到配置文件对象。webpack中所有的资源都可以通过require的方式引入,比如require一张图片,require一个css文件、一个scss文件等。webpack中的loader是一个函数,主要为了实现源码的转换,所以loader函数会以源码作为参数,比如,将ES6转换为ES5,将less转换为css,然后再将css转换为js,以便能嵌入到html文件中,plugin是一个类,类中有一个apply()方法,主要用于Plugin的安装,可以在其中监听一些来自编译器发出的事件,在合适的时机做一些事情。三、webpack打包原理解析webpack通过自定义了一个可以在node和浏览器环境都能执行__webpack_require__函数来模拟Node.js中的require语句,将源码中的所有require语句替换为__webpack_require__,同时从入口文件开始遍历查找入口文件依赖,并且将入口文件及其依赖文件的路径和对应源码映射到一个modules对象上,当__webpack_require__执行的时候,首先传入的是入口文件的id,就会从这个modules对象上去取源码并执行,由于源码中的require语句都被替换为了__webpack_require__函数,所以每当遇到__webpack_require__函数的时候都会从modules对象上获取到对应的源码并执行,从而实现模块的打包并且保证源码执行顺序不变。四、webpack打包流程分析webpack启动文件: webpack首先会找到项目中的webpack.config.js配置文件,并以require(configPath)的方式,获取到整个config配置对象,接着创建webpack的编译器对象,并且将获取到的config对象作为参数传入编译器对象中,即在创建Compiler对象的时候将config对象作为参数传入Compiler类的构造函数中,编译器创建完成后调用其run()方法执行编译。编译器构造函数: 编译器构造函数要做的事:创建编译器的时候,会将config对象传入编译器的构造函数内,所以要将config对象进行保存,然后还需要保存两个特别重要的数据:一个是入口文件的id,即入口文件相对于根目录的相对路径,因为webpack打包输出的文件内是一个匿名自执行函数,其执行的时候,首先是从入口文件开始的,会调用__webpack_require__(entryId)这个函数,所以需要告诉webpack入口文件的路径。另一个是modules对象,对象的属性为入口文件及其所有依赖文件相对于根目录的相对路径,因为一个模块被__webpack_require__(某个模块的相对路径)的时候,webpack会根据这个相对路径从modules对象中获取对应的源码并执行,对象的属性值为一个函数,函数内容为当前模块的eval(源码)。总之,modules对象保存的就是入口文件及其依赖模块的路径和源码对应关系,webpack打包输出文件bundle.js执行的时候就会执行匿名自执行函数中的__webpack_require__(entryId),从modules对象中找到入口文件对应的源码执行,执行入口文件的时候,发现其依赖,又继续执行__webpack_require__(dependId),再从modules对象中获取dependId的源码执行,直到全部依赖都执行完成。 编译器构造函数中还有一个非常重要的事情要处理,那就是安装插件,即遍历配置文件中配置的plugins插件数组,然后调用插件对象的apply()方法,apply()方法会被传入compiler编译器对象,可以通过传入的compiler编译器对象进行监听编译器发射出来的事件,插件就可以选择在特定的时机完成一些事情。编译器run: 编译器的run()方法内主要就是: buildModule和emitFile。而buildModule要做的就是传入入口文件的绝对路径,然后根据入口文件路径获取到入口文件的源码内容,然后对源码进行解析。其中获取源码过程分为两步: 首先直接读出文件中的源码内容,然后根据配置的loader进行匹配,匹配成功后交给对应的loader函数进行处理,loader处理完成后再返回最终处理过的源码。源码的解析,主要是: 将由loader处理过的源码内容转换为AST抽象语法树,然后遍历AST抽象语法树,找到源码中的require语句,并替换成webpack自己的require方法,即webpack_require,同时将require()的路径替换为相对于根目录的相对路径,替换完成后重新生成替换后的源码内容,在遍历过程中找到该模块所有依赖,解析完成后返回替换后的源码和查找到的所以依赖,如果存在依赖则遍历依赖,让其依赖模块也执行一遍buildModule(),直到入口文件所有依赖都buildModule完成。入口文件及其依赖模块都build完成后,就可以emitFile了,首先读取输出模板文件,然后传入entryId和modules对象作为数据进行渲染,主要就是遍历modules对象生成webpack匿名自执行函数的参数对象,同时填入webpack匿名自执行函数执行后要执行的__webpack_require__(entryId)入口文件id。五、实现一个简单的webpack① 让web-pack命令可执行 为了让web-pack命令可执行,我们需要在其package.json中配置bin,属性名为命令名称即web-pack,属性值为web-pack启动文件,即"./bin/index.js",这样web-pack安装之后或者执行npm link命令之后,就会在/usr/local/bin目录下生产对应的命令,使得web-pack命令可以在全局使用,如:// package.json { "bin": { "web-pack": "./bin/index.js" },}② 让web-pack启动文件可以在命令行直接执行 虽然web-pack命令可以执行了,但是该命令链接的文件是"./bin/index.js",即输入web-pack命令执行的是"./bin/index.js"这个js文件,而js文件是无法直接在终端环境下执行的,所以需要告诉终端该文件的执行环境为node,所以需要在"./bin/index.js"文件开头添加上#! /usr/bin/env node,即用node环境执行"./bin/index.js"文件中的内容,如:// ./bin/index.js #! /usr/bin/env node③ 获取配置文件,创建编译器并执行 // ./bin/index.js #! /usr/bin/env nodeconst path = require("path");const config = require(path.resolve("webpack.config.js")); // 获取到项目根目录下的webpack.config.js的配置文件const Compiler = require("../lib/Compiler.js");// 引入Compiler编译器类const compiler = new Compiler(config); // 传入config配置对象并创建编译器对象compiler.run(); // 编译器对象调用run()方法执行④ 编译器构造函数 之前说过,编译器的构造函数主要就是保存config对象、保存入口模块id、保存所有模块依赖(路径和源码映射)、插件安装。// ../lib/Compiler.js class Compiler { constructor(config) { this.config = config; // ① 保存配置文件对象 this.entryId; // ② 保存入口模块id this.modules = {} // ③ 保存所有模块依赖(路径和源码映射) this.entry = config.entry; // 入口路径,即配置文件配置的入口文件的路径 this.root = process.cwd(); // 运行web-pack的工作路径,即要打包项目的根目录 // ④遍历配置的插件并安装 const plugins = this.config.plugins; // 获取使用的plugins if(Array.isArray(plugins)) { plugins.forEach((plugin) => { plugin.apply(this); // 调用plugin的apply()方法安装插件 }); } }}⑤ 编译器run()方法 ...

September 11, 2019 · 5 min · jiezi

使用createreactapp脚手架-npm-run-eject提示确认后输入yes就报错了

一把我们创建的react项目是默认隐藏配置文件的。如果我们想要配置就需要自己手动开启配置。 运行一下命令开启 npm run eject注意:输入y也可能报错, 原因如下:这是个git问题,你的版本库有未提交的文件,因为reject后会多出来一些文件。为了不影响。你应该把这些文件加到ignore里或者删掉。还有错误信息已经非常明显的告诉你具体要怎么做了 处理错误:git init (在项目中生成git仓库,如果已经生成了可忽略这一步,不要带括号的字)git add .git commit -m 'save'npm run eject

September 10, 2019 · 1 min · jiezi

手摸手-Webpack-多入口配置实践

最近在做项目的时候遇到了一个场景:一个项目有多个入口,不同的入口,路由、组件、资源等有重叠部分,也有各自不同的部分。由于不同入口下的路由页面有一些是重复的,因此我考虑使用 Webpack 多入口配置来解决这个需求。 再一次,在网上找的不少文章都不合我的需求,很多文章都是只简单介绍了生产环境下配置,没有介绍开发环境下的配置,有的也没有将多入口结合 vue-router、vuex、ElementUI 等进行配置,因此在下通过不断探坑,然后将思路和配置过程记录下来,留给自己作为笔记,同时也分享给大家,希望可以帮助到有同样需求的同学们~ 1. 目标分析一个项目中保存了多个 HTML 模版,不同的模版有不同的入口,并且有各自的 router、store 等;不仅可以打包出不同 HTML,而且开发的时候也可以顺利进行调试;不同入口的文件可以引用同一份组件、图片等资源,也可以引用不同的资源;代码仓库:multi-entry-vue 示意图如下: 2. 准备工作首先我们 vue init webpack multi-entry-vue 使用 vue-cli 创建一个 webpack 模版的项。文件结构如下: .├── build├── config├── src│   ├── assets│   │   └── logo.png│   ├── components│   │   └── HelloWorld.vue│   ├── router│   │   └── index.js│   ├── App.vue│   └── main.js ├── static├── README.md├── index.html├── package-lock.json└── package.json这里顺便介绍在不同系统下生成目录树的方法: mac 系统命令行生成目录树的方法 tree -I node_modules --dirsfirst ,这个命令的意思是,不显示 node_modules 路径的文件,并且以文件夹在前的排序方式生成目录树。如果报没有找到 tree 命令的错,安装 tree 命令行 brew install tree 即可。windows 系统在目标目录下使用 tree /f 1.txt 即可把当前目录树生成到一个新文件 1.txt 中。首先我们简单介绍一下 Webpack 的相关配置项,这些配置项根据使用的 Webpack 模版不同,一般存放在 webpack.config.js 或 webpack.base.conf.js 中: ...

September 10, 2019 · 5 min · jiezi

从零搭建webpack4reacttypescripteslint脚手架三

处理静态资源js的打包基本处理完了,还有图片、音频等静态资源需要处理。 依然先装依赖: $ npm i -D url-loader file-loader$ npm i -D @svgr/webpack # 顺带支持一下导入svg图片增加webpack配置: // webpack.base.js{ test: /\.svg$/, use: ['@svgr/webpack']},{ test: /\.(jpg|jpeg|bmp|png|webp|gif)$/, loader: 'url-loader', options: { limit: 8 * 1024, // 小于这个大小的图片,会自动base64编码后插入到代码中 name: 'img/[name].[hash:8].[ext]', outputPath: config.assetsDirectory, publicPath: config.assetsRoot }},// 下面这个配置必须放在最后{ exclude: [/\.(js|mjs|ts|tsx|less|css|jsx)$/, /\.html$/, /\.json$/], loader: 'file-loader', options: { name: 'media/[path][name].[hash:8].[ext]', outputPath: config.assetsDirectory, publicPath: config.assetsRoot }}tips: 生产环境需要合理使用缓存,需要拷贝一份同样的配置在webpack.prod.js中,并将name中的hash改为contenthash接下来我们要把public目录里除了index.html以外的文件都拷贝一份到打包目录中: 安装依赖: $ npm i -D copy-webpack-plugin增加配置: // webpack.base.jsconst CopyWebpackPlugin = require('copy-webpack-plugin');plugins: [ // ...other plugins new CopyWebpackPlugin([ { from: 'public', ignore: ['index.html'] } ])]提取公共模块,拆分代码有些模块是公共的,如果不把他拆分出来,那么他会在每一个被引入的模块中出现,我们需要优化与此相关的配置。 ...

September 10, 2019 · 2 min · jiezi

httpswwwjianshucomp9f4a9b1c90c7

前端开发是web前端开发的简称,网上有很多各式各样的解释和描述,今天我从我自己的角度和理解来谈一谈前端开发。 前端开发从业者有以下几种来源:培训中心,后端转前端,美工转前端。大部分前端可能都是有第一种和第三种演变而来,前端开发是一个比较新的岗位,国内国际真正关注前端开发应该是从2005年开始的,至今不过10余年而已。 三要素 Web前端开发技术包括三个要素:HTML、CSS和JavaScript,但随着RIA的流行和普及,Flash/Flex、Silverlight、XML和服务器端语言也是前端开发工程师应该掌握的。Web前端开发工程师既要与上游的交互设计师、视觉设计师和产品经理沟通,又要与下游的服务器端工程师沟通,需要掌握的技能非常多。这就从知识的广度上对Web前端开发工程师提出了要求。如果要精于前端开发这一行,也许要先精十行。然而,全才总是少有的。所以,对于不太重要的知识,我们只需要“通”即可。但“通”到什么程度才算够用呢?对于很多初级前端开发工程师来说,这个问题是非常令人迷惑的。 前端开发的入门门槛其实非常低,与服务器端语言先慢后快的学习曲线相比,前端开发的学习曲线是先快后慢。所以,对于从事IT工作的人来说,前端开发是个不错的切入点。也正因为如此,前端开发领域有很多自学成“才”的同行,但大多数人都停留在会用的阶段,因为后面的学习曲线越来越陡峭,每前进一步都很难。另一方面,正如前面所说,前端开发是个非常新的职业,对一些规范和最佳实践的研究都处于探索阶段。总有新的灵感和技术不时闪现出来,例如CSS sprite、悬浮定位、负边距布局、栅格布局等;各种JavaScript框架层出不穷,为整个前端开发领域注入了巨大的活力;浏览器大战也越来越白热化,跨浏览器兼容方案依然是五花八门。为了满足“高可维护性”的需要,需要更深入、更系统地去掌握前端知识,这样才可能创建一个好的前端架构,保证代码的质量。 技能清单 必须掌握基本的Web前端开发技术,其中包括:CSS、HTML、DOM、javascript、Ajax,jquery,Vue,jquery-mobile,zepto等,在掌握这些技术的同时,还要清楚地了解它们在不同浏览器上的兼容情况、渲染原理和存在的Bug。这是前端工程师的最核心技能,是专做页面效果的技术。如果想更深层次的做好前端开发,那就需要学习和了解更多的东西,比如一些热门的框架backbone,angularjs等;nodejs近几年也越来越火了,同样需要学习。 简单总结 前端开发是一个由易到难的过程,入门简单,进来之后你会发现面前是一片大海,只要想做到更好,总有你不知道的东西,在这个知识爆发的年代,分分钟都有新的东西在孕育和产生,这就需要我们不断的学习和专研,做到不骄不躁,努力专研。 个人格言 任何一件简单的事情,只要愿意坚持认认真真10000个小时,那都将成为这个行业的专家。 原作者:王学兵原文链接:https://www.jianshu.com/p/9f4...

September 10, 2019 · 1 min · jiezi

从零搭建webpack4reacttypescripteslint脚手架一

引言项目github仓库地址: https://github.com/mecoepcoo/ts-react-boilerplate这个系列的文章主要讲述如何从一个空目录建立webpack+react+typescript+eslint脚手架,书写此文时各主要工具的版本为: webpack v4react v16.9typescript v3.5babel v7eslint v6.2本文涉及的内容大致包含: webpack的配置对静态资源(图片,模板等)的处理使react项目支持typescript,eslint,prettier等工具优化webpack配置,减小代码的体积支持不同的css预处理器(less,sass等)一套好用的样式方案使项目支持多个环境切换(开发,测试,预发布,生产等)使用规则来自动约束代码规范优化开发体验一些优化项目性能的建议阅读这个系列的文章需要具备的条件: 你使用过vue,react或angular等任意一种前端框架你了解过vue-cli,create-react-app,angular-cli等任意一种脚手架生成工具你了解webpack的基本原理或用法你有生产环境代码的开发经验,了解生产环境中的代码与自娱自乐代码的区别Why not create-react-app?笔者使用CRA新建项目时,觉得自定义程度不够。尝试过 react-app-rewired + customize-cra 的方案,还是觉得异常繁琐,而且会消耗额外的维护精力,鲁迅说:青年应当有朝气,敢作为,遂自行搭建一个 boilerplate 。 初始化目录我们要从一个空目录开始,先新建这个目录,做一些必要的初始化工作: $ mkdir my-react$ cd my-react$ git init$ npm init新建如下目录结构: react-project config 打包配置public 静态文件夹 index.htmlfavicon.icosrc 源码目录规范git提交协作开发时,git提交的内容如果没有规范,就不好管理项目,我们用 husky + commitlint 来规范git提交。 我们先在根目录下建立 .gitignore 文件,忽略不需要要的文件。 然后安装工具: $ npm i -D husky$ npm i -D @commitlint/clihusky 会为 git 增加钩子,在 commit 时执行一系列操作,commitlint 可以检查 git message 是否符合规则。在 package.json 中增加配置如下: "husky": { "hooks": { "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" }},在根目录新建文件 .commitlintrc.js,根据具体情况配置: ...

September 9, 2019 · 3 min · jiezi

从零搭建webpack4reacttypescripteslint脚手架二

完善webpack打包配置有了webpack的基础配置,还不足以支持打生产环境能够使用的包,我们还需要增加一些配置。 首先,每次打包前最好能把上一次生成的文件删除,这里可以用clean-webpack-plugin插件实现: $ npm i -D clean-webpack-plugin然后修改webpack基础配置: // webpack.base.jsconst { CleanWebpackPlugin } = require('clean-webpack-plugin');module.exports = { plugins: [ new CleanWebpackPlugin(), ]}在生产环境,我们希望部署新版本后能够丢弃缓存,又希望保留没有被改动的文件的缓存,而在开发环境,我们希望完全不使用缓存,因此我们需要在当前配置的基础上,分别扩展生产和开发两套配置。 // webpack.prod.js 生产环境打包配置const merge = require('webpack-merge');const baseWebpackConfig = require('./webpack.base');const { CleanWebpackPlugin } = require('clean-webpack-plugin');module.exports = merge.smart(baseWebpackConfig, { mode: 'production', devtool: sourceMapsMode, output: { filename: 'js/[name].[contenthash:8].js', // contenthash:只有模块的内容改变,才会改变hash值 }, plugins: [ new CleanWebpackPlugin(), ]}// webpack.dev.js 开发环境的配置const merge = require('webpack-merge');const baseWebpackConfig = require('./webpack.base');const config = require('./config');module.exports = merge.smart(baseWebpackConfig, { mode: 'development', output: { filename: 'js/[name].[hash:8].js', publicPath: config.publicPath // 这里可以省略 }, module: { rules: [ { oneOf: [] } ] },}接下来我们编辑build.js,让打包程序真正能够运行起来: ...

September 9, 2019 · 2 min · jiezi

一看就懂之webpack高级配置与优化

一、打包多页面应用所谓打包多页面,就是同时打包出多个html页面,打包多页面也是使用html-webpack-plugin,只不过,在引入插件的时候是创建多个插件对象,因为一个html-webpack-plugin插件对象只能打包出一个html页面。如: module.exports = { entry: { index: "./src/index.js", // 指定打包输出的chunk名为index foo: "./src/foo.js" // 指定打包输出的chunk名为foo }, plugins: [ new HtmlWebpackPlugin({ template: "./src/index.html", // 要打包输出哪个文件,可以使用相对路径 filename: "index.html", // 打包输出后该html文件的名称 chunks: ["index"] // 数组元素为chunk名称,即entry属性值为对象的时候指定的名称,index页面只引入index.js }), new HtmlWebpackPlugin({ template: "./src/index.html", // 要打包输出哪个文件,可以使用相对路径 filename: "foo.html", // 打包输出后该html文件的名称 chunks: ["foo"] // 数组元素为chunk名称,即entry属性值为对象的时候指定的名称,foo页面只引入foo.js }), ]}打包多页面时,关键在于chunks属性的配置,因为在没有配置chunks属性的情况下,打包输出的index.html和foo.html都会同时引入index.js和foo.js,所以必须配置chunks属性,来指定打包输出后的html文件中要引入的输出模块,数组的元素为entry属性值为对象的时候指定的chunk名,如上配置,才能实现,index.html只引入index.js,foo.html只引入foo.js文件二、配置source-mapsource-map就是源码映射,主要是为了方便代码调试,因为我们打包上线后的代码会被压缩等处理,导致所有代码都被压缩成了一行,如果代码中出现错误,那么浏览器只会提示出错位置在第一行,这样我们无法真正知道出错地方在源码中的具体位置。webpack提供了一个devtool属性来配置源码映射。 let foo = 1;console.lg(`console对象的方法名log写成了lg`); // 源文件第二行出错index.js:1 Uncaught TypeError: console.lg is not a function at Object.<anonymous> (index.js:1) at o (index.js:1) at Object.<anonymous> (index.js:1) at o (index.js:1) at index.js:1 at index.js:1源码中出错的位置明明是第二行代码,而浏览器中提示的错误确实在第一行,所以如果代码很复杂的情况下,我们就无法找到出错的具体位置devtool常见的有4种配置:① source-map: 这种模式会产生一个.map文件,出错了会提示具体的行和列,文件里面保留了打包后的文件与原始文件之间的映射关系,打包输出文件中会指向生成的.map文件,告诉js引擎源码在哪里,由于源码与.map文件分离,所以需要浏览器发送请求去获取.map文件,常用于生产环境,如: ...

September 7, 2019 · 4 min · jiezi

sourceMap是个啥

为啥用sourceMap这几天在搞前端错误日志,做过线上发布的都知道,我们发布到生产环境的代码,一般都有如下步骤: 压缩混淆,减小体积多个文件合并,减少HTTP请求数通过编译或者转译,将其他语言编译成JavaScript这三个步骤,都使得实际运行的代码不同于开发代码,不管是 debug 还是捕获线上的报错,都会变得困难重重。 解决这个问题的方法,就是使用sourceMap。 啥是sourceMap简单说,sourceMap就是一个文件,里面储存着位置信息。 仔细点说,这个文件里保存的,是转换后代码的位置,和对应的转换前的位置。 有了它,出错的时候,通过断点工具可以直接显示原始代码,而不是转换后的代码。 sourceMap长啥样通过webpack等工具,我们可以使用 sourceMap,这里不细说配置方法,可以看这里 sourceMap是一个map文件,与源码在同一个目录下。 在压缩代码的最后一行,会有这样的一个引用: //# sourceMappingURL=app.js.map指向的就是我们的map文件。 sourceMap的格式如下: { version : 3, //SourceMap的版本,目前为3 sources: ["foo.js", "bar.js"], //转换前的文件,该项是一个数组,表示可能存在多个文件合并 names: ["src", "maps", "are", "fun"], //转换前的所有变量名和属性名 mappings: "AACvB,gBAAgB,EAAE;AAClB;", //记录位置信息的字符串 file: "out.js", //转换后的文件名 sourcesContent: " \t// The module cache\n", //转换后的代码 sourceRoot : "" //转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空}其他的都很好解释,我们详细说一下mappings属性。 mappings以"AACvB,gBAAgB,EAAE;AAClB;"为例: 每个分号对应转换后源码的一行;每个逗号对应转换后源码的一个位置;AACvB代表该位置转换前的源码位置,以VLQ编码表示;位置对应的原理位置关系的保存经历了诸多步骤和优化,这个不详细说了,想看的可以看这里,我们只说最后的结果。 在每个位置中: 第一位,表示这个位置在【转换后代码】的第几列。第二位,表示这个位置属于【sources属性】中的哪一个文件。第三位,表示这个位置属于【转换前代码】的第几行。第四位,表示这个位置属于【转换前代码】的第几列。第五位,表示这个位置属于【names属性】的哪一个变量。举例假设现在有a.js,内容为feel the force,处理后为b.js,内容为the force feel 以the为例,它在输出中的位置是(0,0),a.js是sources的第1个(这里只是举例),输入中的位置是(0,5),the是names的第2个(这里只是举例)。 那么映射关系为:0 1 0 5 2 最后将 01052 表示为 Base64 VLQ 即可。 ...

August 28, 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

小爱ADMIN系列文章二用Vuecli3mockjs-实现后台管理权限和三级菜单功能

最近完成了我的小爱ADMIN后台管理系统基本功能,同时进行了页面整体布局和样式的全新改版。新增了系统权限功能的实现,同时觉得后台系统所有的菜单都左置,会限制菜单的扩展,因此我改进了三级菜单的显示。 效果演示地址github地址权限功能的实现权限路由思路:根据用户登录的roles信息与路由中配置的roles信息进行比较过滤,生成可以访问的路由表,并通过router.addRoutes(store.getters.addRouters)动态添加可访问权限路由表,从而实现左侧和顶栏菜单的展示。 权限功能的实现步骤:1.给相应的菜单设置默认的roles信息在router/index.js中,给相应的菜单设置默认的roles信息;如下: 给"权限设置"菜单设置的权限为: { path: '/permission', name: 'permission', meta: { title: '权限设置', roles: ['admin', 'editor'] //不同的角色都可以看到 }}给其子菜单"页面权限",设置权限为: { path: 'page', name: 'pagePer', meta: { title: '页面权限', roles: ['admin'] //只有"admin"可以看到该菜单 }, component: () => import('@/page/permission/page'),}给其子菜单"按钮权限"设置权限为: { path: 'directive', name: 'directivePer', meta: { title: '按钮权限', roles:['editor'] //只有"editor"可以看到该菜单 }, component: () => import('@/page/permission/directive'),}2.通过router.beforeEach()进行路由过滤和权限拦截;代码如下: function hasPermission(roles, permissionRoles) { if (roles.indexOf('admin') >= 0) return true if (!permissionRoles) return true return roles.some(role => permissionRoles.indexOf(role) >= 0)}const whiteList = ['/login'] // 不重定向白名单router.beforeEach((to, from, next) => { NProgress.start() // 设置浏览器头部标题 const browserHeaderTitle = to.meta.title store.commit('SET_BROWSERHEADERTITLE', { browserHeaderTitle: browserHeaderTitle }) // 点击登录时,拿到了token并存入了cookie,保证页面刷新时,始终可以拿到token if (getToken('Token')) { if(to.path === '/login') { next({ path: '/' }) NProgress.done() } else { // 用户登录成功之后,每次点击路由都进行了角色的判断; if (store.getters.roles.length === 0) { let token = getToken('Token'); getUserInfo({"token":token}).then().then(res => { // 根据token拉取用户信息 let userList = res.data.userList; store.commit("SET_ROLES",userList.roles); store.commit("SET_NAME",userList.name); store.commit("SET_AVATAR",userList.avatar); store.dispatch('GenerateRoutes', { "roles":userList.roles }).then(() => { // 根据roles权限生成可访问的路由表 router.addRoutes(store.getters.addRouters) // 动态添加可访问权限路由表 next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 }) }).catch((err) => { store.dispatch('LogOut').then(() => { Message.error(err || 'Verification failed, please login again') next({ path: '/' }) }) }) } else { // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓ if (hasPermission(store.getters.roles, to.meta.roles)) { next()// } else { next({ path: '/401', replace: true, query: { noGoBack: true }}) } } } } else { if (whiteList.indexOf(to.path) !== -1) { // 点击退出时,会定位到这里 next() } else { next('/login') NProgress.done() } }})router.afterEach(() => { NProgress.done() // 结束Progress setTimeout(() => { const browserHeaderTitle = store.getters.browserHeaderTitle setTitle(browserHeaderTitle) }, 0)})本系统权限逻辑分析1、路由对象区分权限路由对象和非权限路由对象;初始化时,将非权限路由对象赋值给Router;同时设置权限路由中的meta对象,如:meta:{roles:['admin','editor']},表示该roles所拥有的路由权限; ...

August 28, 2019 · 4 min · jiezi

第三篇-仿写Vue生态系列枚举与双向绑定

( 第三篇 )仿写'Vue生态'系列___" '枚举' 与 '双向绑定' " 本次任务 对'遍历'这个名词进行死磕.对defineProperty进行分析.实现cc_vue的数据双向绑定.为下一篇 Proxy 代替 defineProperty 做预热.一. 'forEach' vs 'map'很多文章都写过他们两个的区别 forEach没有返回值, map会返回一个数组map利于压缩, 因为毕竟只有三个字母.但是这些区别只是表面上的, 我来展示一个有趣的???? <div id="boss"> <div>1</div> <div>2</div> <div>3</div></div><script> let oD = document.getElementById('boss'); // 正常执行 oD.childNodes.forEach(element => {console.log(element); }); // 报错 oD.childNodes.map(element => { console.log(element); });</script>oDs.childNodes 并不是个数组, 他仍然是伪数组, 但是他可以使用forEach, 这个挺唬人的, 第一反应是这两个遍历方法在实现的方式上是不是有什么不同, 但转念一想感觉自己想歪了, 答案其实是 oDs.childNodes这个伪数组形成的时候, 往身上挂了个forEach... 通过上面的问题我有了些思考 map既然返回新数组, 那就说明他空间复杂度会大一些.某些系统提供的伪数组, 本身会挂载forEach但不会挂载map.综上所述, 还是用forEach保险! 但是就想用map怎么办那?1: slice的原理就是一个一个的循环放入一个新数组; let slice = Array.prototype.slice;slice.call(oD.childNodes).map(()=>{})2: 扩展运算符原理不太一样, 但他一样可以把所有元素都拿出来, 接下来我们就对他进行死磕. ...

August 27, 2019 · 5 min · jiezi

第二篇-仿写Vue生态系列模板小故事

( 第二篇 )仿写'Vue生态'系列___'模板小故事.' 本次任务 承上: 完成第一篇未完成的'热更新'配置.核心: 完成'模板解析'模块的相关编写, 很多文章对模板的解析阐述的都太浅了, 本次我们一起来深入讨论一下, 尽可能多的识别用户的语句.启下: 在结构上为'双向绑定'、watch、dep等模块的编写打基础. 最终效果图 一. 模板页面我们既然要开发一个mvvm, 那当然要模拟真实的使用场景, 相关的文件我们放在:'cc_vue/use'路径下, 代码如下: 'cc_vue/use/1:模板解析/index.html', 本篇专门用来展示模板解析的页面'cc_vue/use/1:模板解析/index.js', 本篇专门用来展示模板解析的逻辑代码本来要展示html文件的信息, 但是内容冗长而且没有什么技术可言, 所以不在此展示了. function init(){ new C({ el: '#app', data: { title: '努力学习', ary: [1, 2, 3], obj: { name: '金毛', type: ['幼年期', '成熟期', '完全体'] }, fn() { return '大家好我是: ' + this.obj.name; } } });}export default init;一. 配置文件与简易的热更新之所以说它是简易的, 原因是我们并不会去做到很细致, 比如本次不会去追求每一次的精准更新, 而是每一次都会对整体采取更新, 毕竟本次工程热更新只是一个知识点, 我们还有很多很多更重要的事要做emmmm 一些自己的观点热更新并不是算很神奇, 我之前配置过vuex的热更新相关, 后来总结了一下, 它与回调函数概念差不多, 原理就是当编辑器, 或者是serve检测到你的文件有相应变化的时候, 执行一个回调函数, 这个回调函数里面就是一些重新渲染, 更新dom等等的操作, 你可能会有疑问, vue的热更新做的那么好, 也没看见有什么热更新的回调函数啊, 其实这都归功于'vue-loader', css 热更新考的是css-loader, 他们在处理文件的阶段就把热更新的回调代码注入了js文件里面, 所以我们才会是无感的, 所以没有'loader'帮助我们注入热更新, 那本次我们就自己手动实现.???? ...

August 21, 2019 · 5 min · jiezi

记一次webpack打包

记一次webpack打包前言 公司的一个公众号要做一个H5的活动. 很简单的两个页面, 写完之后, 我想要不要去做一下压缩, 还是直接放上去就好了, 后面一想, 还是做下压缩吧, 正好重新学习下webpack, 以前用webpack 都是人家写好的手脚架, 拿来直接用的, 自己改改, 没啥问题, 但是要自己重新搭一套, 好像也不太会, 所以趁这次机会实验一下.项目详情 由于只是一个小的活动页, 只有三个页面, 所以, 开始写的时候, 框架只采用了 zepto.js, 后面需要一个截屏的功能, 所有又用了 html2canvas. 并且最坑的是,我再开发时并没有采用webpack去开发, 最简单的方式去开发, 搭配nginx.目录结构: less 用less写样式, 实时编译cssjs 逻辑控制libs 用来放第三方库config 有两个文件, utils.js 和 api.jsimage 用来放图片html 文件放在最外层开始进行 webpack 配置 npm init 进行初始化npm install --save-dev webpack webapck-cli (注意: webpack版本使用的是 4.39.2 )根据文档在根目录下添加 webpack.config.js 配置文件在 package.json 中 添加 "build": "webpack --config webpack.config.js"配置入口文件: 在这里由于我有三个页面, 所以配置了三个入口文件, 对应我的三个js文件并且由于加入了webpack的原因, 新增了 src 目录, 将 js 放在src下面配置出口文件: ...

August 21, 2019 · 1 min · jiezi

使用webpackdevserver创建mock-server

项目地址:https://github.com/yuanyuansh... 在开发基于 api 交互、前后端分离的网页应用时,经常会遇到几个问题: 前端页面已经编排好了,但是后台接口还没准备好我们希望服务器返回特定类型的数据,以测试某页面在特定条件下是否存在问题,但作为前端我们一般不会接触到后端代码和数据库,每次都找后端添加模拟数据又很麻烦。为解决这两个问题,最简单的解决办法就是搭建一个 mock server,专门返回需要的模拟数据。 webpack-dev-server 是我们开发 vue、react 时必备的工具,既然是一个服务器,那么我们是不是可以让他实现一个 mock server 的功能。 原理:通过 webpack-dev-server 的 before 钩子,可以在 webpack-dev-server 上添加我们需要的 mock server 功能,而不需要另行搭建服务器。 只需要少许修改就能 webpack-dev-server 当做 mock server 来用,并且对同一 URL 下的 GET、POST、PATCH 等不同的 HTTP METHOD 做分别处理,支持热切换。 使用方法很简单,在 webpack.dev.conf.js 的 devServer 中添加新钩子 before,将所有请求交由 apiMocker 处理,然后当需要使用模拟数据时,只需要将请求的 URL 改为 webpack 服务器上既可。项目地址webpack_api_mocker 安装npm install mocker-api --save-dev使用package.json中配置 "dev-mock": "cross-env MOCK=true webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"webpack.dev.conf.js中配置 devServer: { before (app) { if (process.env.MOCK) { apiMocker(app, path.resolve('mock/mocker'), { proxy: apiDomainMap, changeHost: true }) } } }apiDomainMap.js配置 ...

August 21, 2019 · 1 min · jiezi

laravelmix配置vue的懒加载组件

larave-mix version: v4.1.2 安装依赖yarn add babel-plugin-syntax-dynamic-import --save-devyarn add babel-plugin-dynamic-import-webpack --save-dev.baberc增加: "plugins": [ ["syntax-dynamic-import"] ]webpack.config.js增加: mix.config.webpackConfig.output = { chunkFilename: 'js/[name].bundle.js', publicPath: 'public/dist/js',};引入组件改为://Vue.component('post-content-vue', require('../components/PostContent'));PostContent = ()=>import(/* webpackChunkName: "post-content" */'../components/PostContent');Vue.component('post-content-vue', PostContent);Enjoy.

August 20, 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

webpack-hash-chunkhash-contenthash浅析

1.基本概念bundle 和 chunk是什么? 官网解释:bundle由chunk组成,其中有几种类型(入口 entry chunk child chunk)。通常,chunk会直接对所输出的bundle。但是有些配置并不会产生一对一的关系。 个人理解:chunk就是代码块的意思。多个chunk合在一起,就是bundle,一个bundle可以理解成一个大的js打包后生成的文件,而多个bundle里面的公共代码,或者需要按需加载的代码,就会拆成不同的chunk. module:就是模块,在webpack中一个模块对应一个文件。webpack会从entry开始,递归地找出所有依赖的模块。 类似于 我如果只有一个入口文件,入口文件中也没有任何依赖,只有本文件中的代码,那么这个文件就即是一个module,也是一个chunk,还是一个bundle。 同上,如果我只有一个入口文件,入口文件index.js中引入了react react-dom,如果我们不拆包,那么就是3个module,一个chunk,一个bundle,如果我们把react的东西拆出去打成vendor.js,那么就是3个module,2个chunk,一个bundle。 hash:在 webpack 一次构建中会产生一个 compilation 对象,该 hash 值是对 compilation 内所有的内容计算而来的。 chunkhash:每一个 chunk 都根据自身的内容计算而来。它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。 contenthash:根据文件内容计算而来。 2.具体应用文件指纹 主要是在生成文件的时候用到包括 hash chunkhash contenthash,一般会在设置output filename,以及css文件名的时候使用到。 1.使用hash 2.使用chunkhash我们在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响。 3.使用contenthash 但是 webpack中是如何进行文件内容计算的呢? 是根据moduleId.content来计算的,然后生成对应的hash值。 问题又来了,moduleId是怎么来的呢? eg:module.id === require.resolve('./file.js');它是根据模块排序方法计算的,这是因为每个 module.id 会默认地基于解析顺序(resolve order)进行增量(module是全局的,是为了不同的入口文件,引入同一个module时可以使用缓存,而不必打重复的包)。也就是说,当解析顺序发生变化,ID 也会随之改变,极其不稳定。解决办法 固定moduleId,使用NamedModulesPlugin 插件来固定moduleId. 还有一个Webpack 内部维护了一个自增的 id,每个 chunk 都有一个 id。所以entry新增或者减少入口文件时,也会导致 contenthash变化,因为对应的chunkId变了,解决办法就是固定chunkId。使用HashedModuleIdsPlugin插件。 总结下来,hash受所有代码影响,只要有变化,hash就变了。 chunkhash,受到它自身chunk内容的影响,以及chunkId moduleId的影响。 contenthash,受到它自身文件内容的影响,以及chunkId moduleId的影响。 他们受影响的范围,依次递减。 3.结尾contenthash在webpack4 之前都只能在css上使用,并不能在js上使用,并且现在也只是在production 环境上才能使用,开发环境上我们更多地希望没有缓存,所以会给打包出来的文件,都使用hash值 ...

August 17, 2019 · 1 min · jiezi

webpack-之如何手写一个plugin

1.plugin的概念插件是webpack的“支柱”功能,他在构建流程里注入钩子来实现。在webpack运行的声明周期中会广播许多事件,plugin可以监听这些事件,在特定的时刻调用webpack提供的API执行相应的操作。 2.具体应用webpack 插件是一个具有 apply 方法的 JavaScript 对象。apply 方法会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。 先贴一个我们项目中在使用的plugin例子 源码 webpack.js 所以不用担心,我们写的插件中的apply方法,都肯定会被webpack调用。 那么compiler是什么呢? webpack 的 Compiler 模块是主引擎,它通过 webpack CLI 或 webpack API 或 webpack 配置文件传递的所有选项,创建出一个 compilation 实例。同时它是tabpabled(一个库)的一个扩展类。 同时我们也需要知道 compilation,这个重要的概念。 Compilation 实例继承于 compiler。例如,compiler.compilation 是对所有 require 图(graph)中对象的字面上的编译。这个对象可以访问所有的模块和它们的依赖(大部分是循环依赖)。在编译阶段,模块被加载,封闭,优化,分块,哈希和重建等等。这将是任何编译操作中,重要的生命周期。 我们接着看源码 complier.js中 所以上面例子中compiler.hooks就是找到所有钩子函数,compiler.hooks.compilation中的compilation就是对应的webpack编译过程中的一个钩子函数,我们可以看到compilation就是一个 new SyncHook的一个实例 compiler.hooks.compilation.tap,中的tap是什么意思呢?贴上简化后的代码这里的tap就是给这个钩子函数中注册事件,钩子函数中都是会有各种task(任务),通过tap(name,task)就传入了对应的任务名和任务具体方法。同步的钩子函数使用tap,异步的可以使用tap tapAsync tapPromise。 讲到这里,如果大家理解了上面的描述的话,我们可以知道,最开始贴的那个例子是在做什么了,就是找到compiler的生命周期中的compilation钩子函数,给它注册了一个叫add-cross-origin的方法。 这个方法执行的时候会在compliation (注意这里的compliation是compiler创建的实例对象,而不是刚刚说的那个钩子函数)的htmlWebpackPluginAlterAssetTags钩子函数上注册 add-cross-origin-content事件。 3.自己写一个plugin 4.结尾

August 17, 2019 · 1 min · jiezi

javascript下条件编译的实现基于webpack的jsconditionalcompileloader插件

条件编译,是指 用同一套代码和同样的编译构建过程,根据设置的条件,选择性地编译指定的代码,从而输出不同程序的过程。一般用在C++、Java、C#这样的编译执行语言。对于javascript,我们也可以使用基于webpack的js-conditional-compile-loader插件实现类似的条件编译功能。 我们经常会遇到类似这样的需求:代码需要根据运行环境,运行不同的代码。比如,测试环境可以控制台输出一些信息,生产环境则不提示;同时又不希望输出的代码中存在判断环境的if-else代码使程序包体积增大。项目交付给多个客户使用,而某些客户会有一些定制模块。这些定制模块只给特定用户使用,不希望也一起打包在不相干客户的程序包中,但也不希望给定制客户单独维护一个特殊项目而增加维护成本。使用条件编译的方法,可以优雅地解决这样的问题,同时代码维护方便,发布的程序包中也不会有多余的代码存在。 插件原理js-conditional-compile-loader插件是一个webpack的loader插件,它会在webpack处理js代码之前,将js代码根据设置的条件进行修改,去掉当前条件下不需要的代码,保留需要的代码,从而实现条件编译的功能。 使用步骤可参考这里的中文文档。 1. 安装npm i -D js-conditional-compile-loader2. 配置webpack在rules中为js文件添加loader,作为第一步处理js文件,并配置编译条件。 module: { rules: [ { test: /\.js$/, include: [resolve('src'), resolve('test')], use: [ //step-2 'babel-loader?cacheDirectory', //step-1 { loader: 'js-conditional-compile-loader', options: { isDebug: process.env.NODE_ENV === 'development', // optional, this is default myFlag: process.env.ENV_COMPANY === 'ALI', // any name, used for /* IFTRUE_myFlag ...js code... FITRUE_myFlag */ } }, ] }, //other rules ]}3. 项目代码中使用插件支持IFDEBUG和IFTRUE两个条件编译指令。用法是:在js代码的任意地方以/*IFDEBUG或/*IFTRUE_xxx开头,以FIDEBUG*/或FITRUE_xxx*/结尾,中间是被包裹的js代码。xxx是在webpack中指定的条件属性名,如上面的myFlag。 举个例子,我们用这样的源代码: /* IFTRUE_forAlibaba */var aliCode = require('./ali/alibaba-business.js')aliCode.doSomething()/* FITRUE_forAlibaba */$state.go('win', {dir: menu.winId /*IFDEBUG , reload: true FIDEBUG*/})当webpack中插件的options配置为{isDebug: true, forAlibaba: true}时,构建后输出的内容: ...

August 17, 2019 · 1 min · jiezi

入门-Webpack3-的配置过程

Webpackwebpack 指南React 的 Webpack 配置PS: 文章结尾有完整实例启动方法:Github地址 本次配置练习主要是针对 webpack-v3,切换分支至 webpack@3-react ,每一次 Commits,基本上对应着相应的文件配置,可以对照着瞅瞅。 开始安装配置 Webpack准备环境: ➜ happymmall git:(webpack@3-react) ✗ node -vv11.13.0➜ happymmall git:(webpack@3-react) ✗ npm -v6.10.1➜ happymmall git:(webpack@3-react) ✗ git --versiongit version 2.17.2 (Apple Git-113)➜ happymmall git:(webpack@3-react) ✗ yarn -v1.17.3安装 yarn# homebrew 安装brew install yarn# npm/cnpm 安装npm install cnpm -gcnpm install yarn -g# 测试是否安装成功➜ ~ yarn --version1.17.3常用命令: yarn init # 初始化yarn add [package] # 添加依赖包yarn add [package] --dev # 添加到 devDependenciesyarn remove [package] # 删除依赖包yarn install # 安装所有依赖包安装 webpack@3yarn inityarn add webpack@3.10.0 --dev根目录新建 webpack.config.js,并新建 src/index.js ...

August 7, 2019 · 5 min · jiezi

基于-Webpack-4-多入口生成模板用于服务端渲染的方案及实战

原作者:@LinuxerPHL原链接:基于 Webpack 4 多入口生成模板用于服务端渲染的方案及实战 法律声明警告:本作品遵循 署名-非商业性使用-禁止演绎3.0 未本地化版本(CC BY-NC-ND 3.0) 协议发布。你应该明白与本文有关的一切行为都应该遵循此协议。 这是什么?背景现代化的前端项目中很多都使用了客户端渲染(Client-side Rendering, CSR)的单页面应用(Single Page Application, SPA)。在大多数情况下,它们都应该通过加载 JavaScript 脚本在浏览器中执行以将页面的大部分视图渲染出来,以及获取页面所需要的数据。单页面应用有着许多非常显著的优势,如它们(单页面应用)依赖的公共资源通常仅需加载一次。数据都是通过异步的 JavaScript 与 XML 技术(Asynchoronous JavaScript and XML, Ajax))加载的,异步性能往往非常高。在路由切换时,仅需刷新和(或)更改页面的一部分,而不需要重新加载整个页面以达到切换路由的目的,因此路由的切换在单页面应用中显得比较流畅自然。然而,单页面应用也存在着很多缺陷,它们包括但不限于: 搜索引擎无法收录我们的网页,因为绝大部分的视图和数据都是通过 JavaScript 在浏览器中异步渲染或加载出来的。即使现在有一些搜索引擎爬虫(如 Google)已经具备了爬取单页面应用的能力(即爬虫具备了解析这些 JavaScript 代码的能力),但等到所有搜索引擎爬虫都支持爬取单页面应用显然不是一个好想法;对于业务逻辑复杂一些的单页面应用,它们用于渲染页面的 JavaScript 脚本(可以称为 Bundle)通常体积巨大。试想加载一个上百 KB,甚至几 MB 的 JavaScript 脚本,特别是在没有内容分发网络(Content Delivery Network, CDN)的情况下,首页渲染的时延是非常大的。同构与服务端渲染对于单页面应用上述的缺点,我们可以考虑利用 Webpack 的多入口配置,将原有的单页面应用同构成与原先的前端路由相似甚至相同的目录结构,指定打包后输出的 HTML 模板。在 Webpack 对整个应用打包之后,将根据入口配置从指定的 HTML 模板生成对应的 HTML 文件,交给位于前端页面与后端之间的中间层(通常使用 Node.js 编写,作为服务端渲染(Server-side Rendering, SSR)的服务器)。注意,此时 Webpack 生成的这些 HTML 文件并不能完全被浏览器解析,因为这些文件里还有提供给中间层渲染使用的一些插值,在用户访问中间层路由时,这些 HTML 文件将被用作服务端渲染的模板,将中间层从后端 API 获取的数据按照插值的格式填充(也可以称为“渲染”),最后发送给用户。 进一步理解到目前为止,这个描述还是十分令人困惑。不过我们没有必要一直纠结这些问题,因为下面的图片也许可以帮助我们进一步了解整个流程: ...

August 7, 2019 · 6 min · jiezi

ReactTypeScriptwebpack4多入口配置

资源React-16.8.*react-router-dom-4.3.*TypeScript-3.5.*webpack-4.*eslint-5.16.*项目目录├── dist # 打包结果目录│ ├── demo1 //类别demo1的打包结果│ │ ├── demo1.himl│ │ ├── demo1.js│ │ └── demo1.css│ └── demo2 ... //类别demo2的打包结果├── src # 业务资源文件目录│ ├── category //项目分类│ │ ├── demo1│ │ ├── demo2│ │ └── ...│ ├── components //公共组件│ ├── util //公共资源│ └── custom.d.ts //项目全局变量声明文件├── index.html //项目启动入口├── .gitignore //git忽略文件├── .eslintrc.js //eslint校验配置├── package.json //依赖包├── tsconfig.json //ts配置├── webpack.config.build.js //webpack打包├── webpack.config.base.js //webpack基础配置└── webpack.config.js //项目启动配置前言对于复杂或多人开发的 React 项目来说,管理和使用每个组件的 props 、 state 或许会成为一件让人头痛的事情,而为每一个组件写文档,成本也会比较大,对项目的开发效率也不是最理想的。 ...

August 7, 2019 · 3 min · jiezi

webpack4手动搭建vue

本文将介绍如何用webpack4搭建一个vue的项目,webpack基本语法这里不做介绍,有问题可以参考webpack中文文档 1.创建一个文件夹,我们暂且称为webpackTest,打开终端,并进入到改文件目录下,初始化npm 项目, 这一步执行完会出现一个package.json的文件 npm init2.安装所需要的依赖:webpack vue vue-loader css-loader vue-template-compiler npm install webpack vue vue-loader css-loader vue-template-compiler3.创建文件夹src,并在里面创建两个文件App.vue和index.js3.1 src/App.vue下的代码为 <template> <div id="app">{{text}}</div> </template> <script> export default { data () { return { text: "hello word" } } } </script>3.2 src/index.js下的代码为 import Vue from 'vue'; import App from './App.vue'; const root = document.createElement('div'); document.body.appendChild(root); new Vue({ render : (h) => h(App) }).$mount(root)4.安装HtmlWebpackPlugin,该插件可自定生成index.html,也可以自定义html模板,想了解更多查看HtmlWebpackPlugin npm install --save-dev html-webpack-plugin5.新建webpack.config.js,代码如下 const path = require('path')// 新加入 VueLoaderPluginconst VueLoaderPlugin = require('vue-loader/lib/plugin')var HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = { entry: path.join(__dirname, 'src/index.js'), output: { filename: 'bundle.js', path: path.join(__dirname, 'dist') }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' },{ test: /\.css$/, loader: 'css-loader' } ] }, plugins: [ new VueLoaderPlugin(), new HtmlWebpackPlugin() ]}6.在package.js > script下添加代码如下: ...

July 15, 2019 · 1 min · jiezi

Webpack基本功能理解以及使用

对于前端工程化,webpack一个神奇的工具,既然是个神奇的工具。那我们保留我们的好奇心,来聊一聊它,首先我们要搞清楚webpack到底是用来解决什么问题的,然后我们来看看它到底是怎么做的,最后来看看它的一些基本用法,下面就来侃一侃。 什么是webpack以及webpack的作用 (解决什么问题)如今web前端的业务功能越来越复杂,实现方式也越来越丰富,在web页面开发过程中我们通常会引用很多第三方模块以及一些拓展语言(stylus,Scss, JSX...)来简化开发难度,而这些第三方模块和一些拓展语言浏览器不能直接识别,所以要通过经过打包过程生成可以让浏览器识别的格式。 就像一幢居民楼,要建起这样一座居民楼,最基本的材料是砖、瓦、钢筋、混凝土。而要组合这些材料形成一幢建筑,肯定是有一定的方法流程以及工具的,比如第一步先搭建地基,后面用塔吊不断的在地基上叠加完善就形成了一幢建筑。在这个过程中,用到的方法流程以及工具起到的作用就类似于webpack。 进入正题,webpack其实就是一个JavaScript模块集成工具,同时具有压缩文件以及优化文件结构的作用。经过webpack打包生成的bundle包,可被浏览器识别解析。 在这个过程中,会用到一些loaders解析工具用来预处理一些模块以及拓展语言(例如:stylus、Scss...),这些工具的配置使用都是在webpack中完成的。其中常用的loaders工具有:style-loader、 css-loader、 stylus-loader。 webpack实现原理 (怎么做的) 原理的理解可以参照上图。webpack的最核心的原理: 1、一切皆模块 2、按需加载。 一切皆模块 webpack会将源程序按照程序结构分割成一个个独立的小模块,当需要这些小模块时,进行组合重构,避免冗余,达到重复利用。按需加载 传统的打包工具是将所有模块编译生成一个庞大的bundle.js文件,这样形成的打包文件体积过于庞大,而webpack通过异步加载可以实现按需加载,减小了打包后的体积。webpack的使用方法 (怎么用它)在使用webpack之前首先要理解四个基本概念: 1. 入口(entry)webpack要实现打包,首先我们得指定它的入口,指定入口后,webpack才会找出那些模块和库是入口起点(直接和间接)的依赖。接下来我们来看一个最简单的entry的配置例子。 webpack.base.config.js module.exports = { entry: './src/main.js'};2. 出口(output)出口即配置打包后的输出文件路径,以及如何命名这些文件,默认值为 ./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 output 字段,来配置这些处理过程: webpack.base.config.js const path = require('path');module.exports = { entry: './src/main.js', output: { path: path.resolve(__dirname, 'dist'), // 输出路径 filename: 'output.bundle.js' // 输出文件名 }};3. loaderloader能够将非JavaScript文件转化为webpack识别的JavaScript文件,比如讲图片转化为JavaScript可调用的格式,或者将一些扩展语言文件转化为浏览器可识别的文件格式。本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。在更高层面,在 webpack 的配置中 loader 有两个目标: test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。use 属性,表示进行转换时,应该使用哪个 loader。webpack.config.js const path = require('path');const config = { output: { filename: 'my-first-webpack.bundle.js' }, module: { rules: [ { test: /\.txt$/, use: 'raw-loader' } ] }};module.exports = config;4. 插件(plugins)loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。 ...

July 15, 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

NPM包管理器

NPM安装和定义NPM(全称 Node Package Manager,即“node包管理器”)是Node.js默认的、以JavaScript编写的软件包管理系统。以上是维基百科给NPM的定义。理解软件包管理系统。软件包管理系统是在计算机中自动安装、配制、卸载和升级软件包的工具组合,在各种系统软件和应用软件的安装管理中均有广泛应用。比如:PHP中composer、JAVA中maven、LIUNX系统中的yum NPM软件包管理系统由3部分组成: 网站 可以通过https://www.npmjs.com网站,搜索包、管理自己的包和个人信息。 注册表(registry)一个巨大的数据库,保存着每个包(package)的信息。 命令行工具 (CLI)通过命令行和终端运行,开发者通过CLI与NPM打交道。在安装Node.js时会默认安装NPM包管理器,NPM完全由Javascript写成,和模块没啥区别,所以我们可以npm install -g npm 更新安装自己。 随着前端工程化、模块化的快速发展,前端开发已经离不开npm包管理器了。在我们使用前端各大框架Vue、React、Angluar时,我们只需要npm install [package name]就可以安装使用我们需要的模块包,非常便捷高效。 通过下图我们就能看出npm之火爆。总包量100多万,周下载量114亿。来自各大洲的开源软件开发者都在使用npm借鉴学习或者分享。同时也是Node.js获得成功重要原因之一。 NPM命令以及使用 以上图片罗列出NPM所有的命令,捡几个常用的命令学习; npm install 安装模块安装形式: npm install (with no args, in package dir)npm install [<@scope>/]<pkg>npm install [<@scope>/]<pkg>@<tag>npm install [<@scope>/]<pkg>@<version>npm install [<@scope>/]<pkg>@<version range>npm install <folder>npm install <tarball file>npm install <tarball url>npm install <git:// url>npm install <github username>/<github project>alias: i,addcommon options: [-S|--save|-D|--save-dev|-O|--save-optional] [-E|--save-exact][--no-save][-B|--save-bundle][--dry-run]其中:-S与--save 安装到dependenices生产环境中,对应package.json中的dependencies //npm i -S ecpress || npm install --save express"dependencies": { "express": "^4.17.1",}-D与--save-dev 安装到devDependenices开发环境中,对应package.json中的devDependencies ...

July 14, 2019 · 5 min · jiezi

依赖Nodejs

背景在前端历史演变中已经提到,Nodejs的爆发从2009年开始。Nodejs的出现,基于作者Ryan Dahl 对Web开发高性能的追求,要达到高性能,异步IO/事件驱动是基本原则。对比一些高级语言,最终选择Javascript作为开发语言,由于Javascript天生的事件驱动和单线程,奠定了Nodejs编写高性能Web服务轻而易举。 简单了解NodejsNode.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。 Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。 以上是官方给出的解释,惯例我们还是抽取关键词理解。 Chrome V8 引擎介绍V8 引擎是Chrome于2008年9月2日发布开源。V8使用C++开发,相比其它的JavaScript的引擎转换成字节码或解释执行,V8将其编译成原生机器码(IA-32, x86-64, ARM, or MIPS CPUs),并且使用了如内联缓存(inline caching)等方法来提高性能。有了这些功能,JavaScript程序在V8引擎下的运行速度媲美二进制程序。Chrome浏览器在Webkit渲染引擎中使用v8引擎来提高浏览器的渲染性能。上图是webkit大致结构,红色部分是webkit的默认引擎,在谷歌系列产品中被替换为v8引擎;Nodejs是站在“巨人的肩膀”上进行一系列的封装,它的高性能,离不开Chorme V8引擎。 JavaScript 运行环境Javascript 是一个静态脚本语言,运行时必须要借助于引擎才能运行。Javascript 运行环境一般分为两种: 浏览器运行环境 ( 通常我们写的js代码要在浏览器中才能运行)非浏览器运行环境 (比如Nodejs,借助于V8引擎实现运行的环境) 事件驱动我们在Javascript中注册个事件(回调函数)。但这个事件不是马上执行。只有等事件被触发的时候,才会去执行这个事件(回调函数)。这种形式就是事件驱动。 非阻塞 I/O 阻塞:前一个程序未执行完就得一直等待。比如当你打电话问个问题时,那边说你等等我给你查查,这时候你电话仍然是挂起的,等待等待,直到拿到结果。 非阻塞:前一个程序未执行完时可以挂起,继续执行其他程序,等到使用时再执行。比如当你打电话过去问一个问题,然后挂电话,等那边找到结果就打电话给你。查问题这段时间,你该干嘛就干嘛。I/O: 磁盘的写入(in)磁盘的读取(out)。在程序执行过程中必然要进行很多I/O操作,读写文件、输入输出、请求响应等等。I/O操作时最费时,举个例子,你要读一个文件,整个线程都暂停下来,等待文件读完后继续执行。换言之,I/O操作阻塞了代码的执行,极大地降低了程序的效率。在Nodejs里面单线程可以通过回调函数(事件驱动)来做异步操作,达到非阻塞I/O的效果。 安装Nodejs可以在官网自行选择安装包下载MacOS用户建议使用brew安装#安装brew install -g node#卸载brew uninstall nodejs使用nvm安装管理Nodejs版本curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash# orwget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash# 放入环境变量source ~/.bash_profile# 判断是否安装成功nvm list# 安装nodenvm install 6.14.4 # or 10.10.0, 8.9.1, etc# 判断node安装成功node -vnpm -v Nodejs 的简单使用 模块化规范 CommonJs在前端模块化中已经对CommonJs做了介绍,并使用exports导出模块,require引入模块,实现了一个简单案例。 ...

July 14, 2019 · 1 min · jiezi

构建一个用于创建组件库的项目脚手架工具类-Vuecli3

缘起最近公司内部想搭建一个私有的 npm 仓库,用于将平时用到次数相当频繁的工具或者组件独立出来,方便单独管理,随着项目的规模变大,数量变多,单纯的复制粘粘无疑在优雅以及实用性上都无法满足我们的需求,所以进一步模块化是必然的。 但是一个组件库的建立其实是一个非常麻烦的过程,基础 webpack 的配置不用多说,接着你还要配合增加一些 es-lint 之类的工具来规范化团队成员的代码。在开发过程中,你自然需要一个目录来承载使用示例,方便 dev 这个组件,随后呢,你还得建立一个打包规范,发布到私有 npm 仓库中。 如此一来,必然大大降低我们的积极性,所以不如创建一个用于建立模块包的脚手架工具,方便我们项目的初始化。 tips:最终成品在底部 私有 NPM这里简单提及一下 私有 npm 的搭建。 npm i verdaccio -gpm2 start verdaccio推荐配合 nrm 使用 快速切换仓库地址 verdaccio github 还整个意大利名,属实洋气。 工具在进入正题之前,我先介绍一些要点和工具,有了这写关键点,写起来其实就相当简单了。 npm bin大家有没有想过一些全局安装的工具,他是如何做到在命令行里面自由调用的呢? 事实上这个东西是 npm 提供的链接功能 // package.json{ "name": "lucky-for-you", "bin": { "lucky": "bin/lucky" }}当这样的一个模块被发布之后,一旦有人使用 -g 参数全局安装 sudo npm i luck-for-you -g/usr/local/bin/lucky -> /usr/local/lib/node_modules/luckytiger-package-cli/bin/lucky # npm 帮你进行链接 npm 事实上会帮你进行一次链接,链接到你操作系统的 Path 之中,从而但你敲出 Lucky 这个命令的时候,能从 path 中成功找到对应的程序 ...

July 11, 2019 · 5 min · jiezi

Nuxtjs服务端渲染实践搭建一个blog

关于SSR的简介SSR,即服务端渲染,这其实是旧事重提的一个概念,我们常见的服务端渲染,一般见于后端语言生成的一段前端脚本,如:php后端生成html+jsscript内容传递给浏览器展现,nodejs在后端生成页面模板供浏览器呈现,java生成jsp等等。 Vuejs、Reactjs、AngularJs这些js框架,原本都是开发web单页应用(SPA)的,单页应用的好处就是只需要初次加载完所有静态资源便可在本地运行,此后页面渲染都只在本地发生,只有获取后端数据才需要发起新的请求到后端服务器;且因为单页应用是纯js编写,运行较为流畅,体验也稍好,故而和本地原生应用结合很紧密,有些对页面响应流畅度要求不是特别苛刻的页面,用js写便可,大大降低了app开发成本。 然而单页应用并不支持良好的SEO,因为对于搜索引擎的爬虫而言,抓取的单页应用页面源码基本上没有什么变化,所以会认为这个应用只有一个页面,试想一下,一个博客网站,如果所有文章被搜索引擎认为只有一个页面,那么你辛辛苦苦写的大多数文章都不会被收录在里面的。 SSR首先解决的就是这个问题,让人既能使用Vuejs、Reactjs来进行开发,又能保证有良好的SEO,且技术路线基本都是属于前端开发栈序列,语言语法没有多大变化,而搭载在Nodejs服务器上的服务端渲染又可以有效提高并发性能,一举多得,何乐而不为? ps:当然,目前某些比较先进的搜索引擎爬虫已经支持抓取单页应用页面了,比如谷歌。但并不意味着SSR就没用了,针对于资源安全性要求比较高的场景,搭载在服务器上的SSR有着天然的优势。关于Nuxtjs这里是官方介绍,Nuxtjs是诞生于社区的一套SSR解决方案,是一个比较完备的Vuejs服务端渲染框架,包含了异步数据加载、中间件支持、布局支持等功能。 关于nuxtjs,你必须要掌握以下几点知识: vuejs、vue-router、vuex等nodejs编程webpack构建前端工程babel-loader如果想使用进程管理工具,推荐使用pm2管理nodejs进程,安装方式为:npm install -g pm2搭建一个blog准备好工具推荐下载 这里iview将作为一个插件在nuxtjs项目中使用。 注意几个配置:nux.config.js module.exports = { /* ** Headers of the page */ head: { title: '{{ name }}', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: '{{escape description }}' } ], link: [ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } ] }, plugins: [ {src: '~plugins/iview', ssr: true} ], /* ** Customize the progress bar color */ loading: { color: '#3B8070' }, /* ** Build configuration */ build: { /* ** Run ESLint on save */ extend (config, { isDev, isClient }) { if (isDev && isClient) { config.module.rules.push({ enforce: 'pre', test: /\.(js|vue)$/, loader: 'eslint-loader', exclude: /(node_modules)/ }) } } }}plugins文件夹下,加入iview.js ...

July 11, 2019 · 6 min · jiezi