×

webpack原理 前端

webpack原理(地址库变化 怎么更新前端编码文件)

admin admin 发表于2022-09-05 19:34:28 浏览115 评论0

抢沙发发表评论

本文目录

地址库变化 怎么更新前端编码文件


这个是组内一位同学在平时开发中,发现调试不便,为团队开发的热更新工具。很厉害,文章中的技术实现内容也是我了解了他的具体实现思路后,整理出来的。
工具源码 EHU(esl-hot-update)
热更新是什么
热更新就是当你在开发环境修改代码后,不用刷新整个页面即可看到修改后的效果。
如果你的项目中使用了webpack的话,你会很幸运,借助webpack-dev-server插件可以实现项目的热更新。
解决的问题
对于大型的系统级别项目会有下面几个特点
模块化(AMD)模式的广泛使用后,开发环境散文件特别多,很容易上百,一不小心还能上千
初始化的内容特别多,各种底层库,ui库等等
这两个特点直接导致每次调试后,刷新会很慢。如果初始化的js达到上千的数量级,每一次重新刷新都是5s,10s,甚至20s的等待。
而热更新的目的就是为了在一定程度上减少这5s,10s,甚20s的浪费。
遇到的问题
我们使用的是百度自己的开发环境工具edp,首先他不支持热更新
我们使用的AMD实践也是百度自己的esl,而且即使是requirejs也暂时没有找到对应的热更新策略,假如requirejs有对应的,我们也无法直接使用
所以最终的结论是我们自己去实现一个基于我们自己业务的。这样我们考虑的面不用太广,并且解决方案的更有针对性,即面向我们现有的业务框架。最重要的是可以尝试修改底层框架做配合。
等待路踩通了,我们再去考虑普适性。
解决的思路
从ehu/package.json 这个文件中,我们就可以看出一些具体的思路
需要一个watch功能,即能够监听到文件的修改
socket.io通知浏览器处理文件的改变
修改esl这个文件,达到能够实时更新的效果
当时最简单的考虑,就是文件改变了后,能够通知浏览器,浏览器去重新load这个文件并且执行一次。这个时候再重新去打开这个模块或者功能后,会发现新load的代码在执行后会覆盖上一次的。
所以当时的我的第一直觉是,esl重复require时,如果后面一次会覆盖前面的,那么可以通过简单的覆盖思路去尝试,结果发现覆盖不了。经过验证,发现是esl内部维护了一个map,即require过的模块会存起来。我们如果希望更新这个模块,只能将map中的对应模块名删除。(后面会详细讲述esl的改造)
对于工具的要求
对应这个工具,我当时也提出了几个要求
esl必然是需要修改的,但是如何对开发人员透明?首先是不能让大家都做这种修改。
页面中也必须加入socket.io支持,那么我们如何在不影响其他人员开发的情况下加入?
我们做的属于beta版本,如何选择性的使用?ehu工具和以前的开发模式随意切换?
安装方便,能否只是作为一个工具,即插即用,不需要繁琐的配置?
如何使用
npm install -g ehu(mac下需要sudo,windows下需要管理员权限)
在原来执行edp webserver start命令的路径 执行 ehu(不再需要执行 edp webserver start)
原来端口号8848修改为8844(原8848依旧可以使用,但不支持热更新)
首先使用的方式很简单,为此特意将工具打包到npm上,以后就算有升级,仅仅需要大家update即可。
另外从使用角度,也尽量集成化(一句命令行即可),避免为了这个工具的使用而做太多额外的事情。
依赖的框架 “dependencies“: { “async“: “^1.5.0“, “commander“: “^2.9.0“, “express“: “^4.13.3“, “express-

前端小白想问,jsp后面是什么意思,怎么用求大神解答


现在前端用Webpack打包JS和其它文件已经是主流了,加上Node的流行,使得前端的工程方式和后端越来越像。所有的东西都模块化,最后统一编译。Webpack因为版本的不断更新以及各种各样纷繁复杂的配置选项,在使用中出现一些迷之错误常常让人无所适从。所以了解一下Webpack究竟是怎么组织编译模块的,生成的代码到底是怎么执行的,还是很有好处的,否则它就永远是个黑箱。当然了我是前端小白,最近也是刚开始研究Webpack的原理,在这里做一点记录。
编译模块
编译两个字听起来就很黑科技,加上生成的代码往往是一大坨不知所云的东西,所以常常会让人却步,但其实里面的核心原理并没有什么难。所谓的Webpack的编译,其实只是Webpack在分析了你的源代码后,对其作出一定的修改,然后把所有源代码统一组织在一个文件里而已。最后生成一个大的bundle JS文件,被浏览器或者其它Javascript引擎执行并返回结果。
在这里用一个简单的案例来说明Webpack打包模块的原理。例如我们有一个模块mA.js
var aa = 1; function getDate() { return new Date(); } module.exports = { aa: aa, getDate: getDate }
我随便定义了一个变量aa和一个函数getDate,然后export出来,这里是用CommonJS的写法。
然后再定义一个app.js,作为main文件,仍然是CommonJS风格:
var mA = require(’./mA.js’); console.log(’mA.aa =’ + mA.aa); mA.getDate();
现在我们有了两个模块,使用Webpack来打包,入口文件是app.js,依赖于模块mA.js,Webpack要做几件事情:
从入口模块app.js开始,分析所有模块的依赖关系,把所有用到的模块都读取进来。 每一个模块的源代码都会被组织在一个立即执行的函数里。 改写模块代码中和require和export相关的语法,以及它们对应的引用变量。 在最后生成的bundle文件里建立一套模块管理系统,能够在runtime动态加载用到的模块。
我们可以看一下上面这个例子,Webpack打包出来的结果。最后的bundle文件总的来说是一个大的立即执行的函数,组织层次比较复杂,大量的命名也比较晦涩,所以我在这里做了一定改写和修饰,把它整理得尽量简单易懂。
首先是把所有用到的模块都罗列出来,以它们的文件名(一般是完整路径)为ID,建立一张表:
var modules = { ’./mA.js’: generated_mA, ’./app.js’: generated_app }
关键是上面的generated_xxx是什么?它是一个函数,它把每个模块的源代码包裹在里面,使之成为一个局部的作用域,从而不会暴露内部的变量,实际上就把每个模块都变成一个执行函数。它的定义一般是这样:
function generated_module(module, exports, webpack_require) { // 模块的具体代码。 // ... }
在这里模块的具体代码是指生成代码,Webpack称之为generated code。例如mA,经过改写得到这样的结果:
function generated_mA(module, exports, webpack_require) { var aa = 1; function getDate() { return new Date(); } module.exports = { aa: aa, getDate: getDate } }
乍一看似乎和源代码一模一样。的确,mA没有require或者import其它模块,export用的也是传统的CommonJS风格,所以生成代码没有任何改动。不过值得注意的是最后的module.exports = ...,这里的module就是外面传进来的参数module,这实际上是在告诉我们,运行这个函数,模块mA的源代码就会被执行,并且最后需要export的内容就会被保存到外部,到这里就标志着mA加载完成,而那个外部的东西实际上就后面要说的模块管理系统。
接下来看app.js的生成代码:
function generated_app(module, exports, webpack_require) { var mA_imported_module = webpack_require(’./mA.js’); console.log(’mA.aa =’ + mA_imported_module[’aa’]); mA_imported_module[’getDate’](); }
可以看到,app.js的源代码中关于引入的模块mA的部分做了修改,因为无论是require/exports,或是ES6风格的import/export,都无法被JavaScript解释器直接执行,它需要依赖模块管理系统,把这些抽象的关键词具体化。也就是说,webpack_require就是require的具体实现,它能够动态地载入模块mA,并且将结果返回给app。
到这里你脑海里可能已经初步逐渐构建出了一个模块管理系统的想法,我们来看一下webpack_require的实现:
// 加载完毕的所有模块。 var installedModules = {}; function webpack_require(moduleId) { // 如果模块已经加载过了,直接从Cache中读取。 if (installedModules[moduleId]) { return installedModules[moduleId].exports; } // 创建新模块并添加到installedModules。 var module = installedModules[moduleId] = { id: moduleId, exports: {} }; // 加载模块,即运行模块的生成代码, modules[moduleId].call( module.exports, module, module.exports, webpack_require); return module.exports; }
注意倒数第二句里的modules就是我们之前定义过的所有模块的generated code:
var modules = { ’./mA.js’: generated_mA, ’./app.js’: generated_app }
webpack_require的逻辑写得很清楚,首先检查模块是否已经加载,如果是则直接从Cache中返回模块的exports结果。如果是全新的模块,那么就建立相应的数据结构module,并且运行这个模块的generated code,这个函数传入的正是我们建立的module对象,以及它的exports域,这实际上就是CommonJS里exports和module的由来。当运行完这个函数,模块就被加载完成了,需要export的结果保存到了module对象中。
所以我们看到所谓的模块管理系统,原理其实非常简单,只要耐心将它们抽丝剥茧理清楚了,根本没有什么深奥的东西,就是由这三个部分组成:
// 所有模块的生成代码 var modules; // 所有已经加载的模块,作为缓存表 var installedModules; // 加载模块的函数 function webpack_require(moduleId);
当然以上一切代码,在整个编译后的bundle文件中,都被包在一个大的立即执行的匿名函数中,最后返回的就是这么一句话:
return webpack_require(‘./app.js’);
即加载入口模块app.js,后面所有的依赖都会动态地、递归地在runtime加载。当然Webpack真正生成的代码略有不同,它在结构上大致是这样:
(function(modules) { var installedModules = {}; function webpack_require(moduleId) { // ... } return webpack_require(’./app.js’); }) ({ ’./mA.js’: generated_mA, ’./app.js’: generated_app });
可以看到它是直接把modules作为立即执行函数的参数传进去的而不是另外定义的,当然这和上面的写法没什么本质不同,我做这样的改写是为了解释起来更清楚。
ES6的import和export
以上的例子里都是用传统的CommonJS的写法,现在更通用的ES6风格是用import和export关键词,在使用上也略有一些不同。不过对于Webpack或者其它模块管理系统而言,这些新特性应该只被视为语法糖,它们本质上还是和require/exports一样的,例如export:
export aa // 等价于: module.exports[’aa’] = aa export default bb // 等价于: module.exports[’default’] = bb
而对于import:
import {aa} from ’./mA.js’ // 等价于 var aa = require(’./mA.js’)[’aa’]
比较特殊的是这样的:
import m from ’./m.js’
情况会稍微复杂一点,它需要载入模块m的default export,而模块m可能并非是由ES6的export来写的,也可能根本没有export default,所以Webpack在为模块生成generated code的时候,会判断它是不是ES6风格的export,例如我们定义模块mB.js:
let x = 3; let printX = () =》 { console.log(’x = ’ + x); } export {printX} export default x
它使用了ES6的export,那么Webpack在mB的generated code就会加上一句话:
function generated_mB(module, exports, webpack_require) { Object.defineProperty(module.exports, ’__esModule’, {value: true}); // mB的具体代码 // .... }
也就是说,它给mB的export标注了一个__esModule,说明它是ES6风格的export。这样在其它模块中,当一个依赖模块以类似import m from ’./m.js’这样的方式加载时,会首先判断得到的是不是一个ES6 export出来的模块。如果是,则返回它的default,如果不是,则返回整个export对象。例如上面的mA是传统CommonJS的,mB是ES6风格的:
// mA is CommonJS module import mA from ’./mA.js’ console.log(mA); // mB is ES6 module import mB from ’./mB.js’ console.log(mB);
我们定义get_export_default函数:
function get_export_default(module) { return module && module.__esModule? module[’default’] : module; }
这样generated code运行后在mA和mB上会得到不同的结果:
var mA_imported_module = webpack_require(’./mA.js’); // 打印完整的 mA_imported_module console.log(get_export_default(mA_imported_module)); var mB_imported_module = webpack_require(’./mB.js’); // 打印 mB_imported_module[’default’] console.log(get_export_default(mB_imported_module));
这就是在ES6的import上,Webpack需要做一些特殊处理的地方。不过总体而言,ES6的import/export在本质上和CommonJS没有区别,而且Webpack最后生成的generated code也还是基于CommonJS的module/exports这一套机制来实现模块的加载的。
模块管理系统
以上就是Webpack如何打包组织模块,实现runtime模块加载的解读,其实它的原理并不难,核心的思想就是建立模块的管理系统,而这样的做法也是具有普遍性的,如果你读过Node.js的Module部分的源代码,就会发现其实用的是类似的方法。这里有一篇文章可以参考。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
您可能感兴趣的文章:探索webpack模块及webpack3新特性关于webpack2和模块打包的新手指南(小结)详解react-webpack2-热模块替换[HMR]webpack配置sass模块的加载的方法详解用webpack把我们的业务模块分开打包的方法Webpack常见静态资源处理-模块加载器(Loaders)+ExtractTextPlugin插件详解webpack异步加载业务模块jQuery 移动端拖拽(模块化开发,触摸事件,webpack)

webpack承担了哪些工作原理功能


承担了很多前端代码的自动化生成的工作。
搭配很多loader,可以完成不同的事情。
比如说把繁杂的项目中用到的js打包并压缩成你设定的一个或多个文件去发布到生产环境。
比如把react中引用的图片压缩并抽出来项目中引用到的。没引用到的,不会拿过来。
比如CSS Modules,单个组件开发时用单独的css定义,最终打包成一个css文件里,而没用到的css不会打包。而且通过设置,不用担心各个文件中的同名css打包到一个css里后重复。
less转css
sass转css
PostCSS转css
es6写的js,通过babel转成浏览器可执行的es5
......
原理,其实就是用程序代替人的工作。比如我们开发的时候经常是css写到一个或几个文件,随着迭代,里面肯定就产生很多垃圾不再用的代码,而用人去识别很累,如果用CSS Modules,把css也模块化,随着迭代,不再引用到的,就不会再打包进去,不用人来识别了。同理,浏览器是不识别sass这种的,程序来帮你转成浏览器可读的css。不管用什么程序,其实都可以开发这样的一个解释器,其实就是一个翻译转化的过程。只不过当前前端的发展需要和node的便捷加上npm方便的分发,所以在node中,大量的工具出现了。
其实原理和功能,就是用程序代替人做那些事,由程序翻译成普通浏览器可执行的代码。
-webpack原理

网页制作都包括哪些技术


1、活版印刷

一个成功的网站只有很少的字体是类似的款式,而不是使用的字体。最好的网站应该使用无衬线和无衬线字体,而不是两者的结合。

印刷术的网站也应该小心使用的字体,好的设计会加入一些类似的字体,而不是一个范围内型面。大多数浏览器都能识别特定数量的安全字体,这样设计主要是利用以避免并发症的发生。

2、代码质量

当创建一个网站,它是很好的做法,以符合标准。这包括代码中的错误,代码为更好的布局,以及确保你的ID和类的正确识别。这通常是通过描述指定元件做什么。

不符合标准的网站无法使用或容易出错的,标准可以涉及到正确的页面布局的可读性,以及确保适当地关闭了编码元素。DOCTYPE声明,这是用来突出显示代码中的错误。系统识别错误和不符合网页设计标准。

3、视觉设计

良好的视觉设计网站上的标识,其目标市场。这可以是一个年龄组或特定的文化链,因此,设计人员应了解其受众的趋势。设计人员也应该明白他们设计的,这意味着一个企业网站不应该被设计成一个社交媒体网站,例如相同类型的网站。-前端

4、交互设计

对于网站来说,好的交互设计对网页设计的整体起着至关重要的作用,一个好的交互不仅能大大提升页面整体的设计效果,增强页面的“活跃度”,还能有效提高用户体验,“屏蔽”单调的操作流程,使得产品、文化的表现都能最大化的呈现并传达到相关的受众。-webpack原理

5、内容更新

企业Web站点建立后,要不断更新网页内容。站点信息的不断更新,让浏览者了解企业的发展动态和网上职务等,同时也会帮助企业建立良好的形象。

参考资料来源:百度百科-网页设计


怎么解决webpack@1


最近由于用着html-webpack-plugin觉得很不爽,于是乎想自己动手写一个插件。原以为像gulp插件一样半天上手一天写完,但令人郁闷的是完全找不到相关的文章。一进官方文档却是被吓傻了。首先是进入 how to write a plugin 看了一页简单的介绍。然后教程会告诉你,你需要去了解compiler和compilation这两个对象,才能更好地写webpack的插件,然后作者给了github的链接给你,让你去看源代码,我晕。不过幸好最后给了一个 plugins 的API文档,才让我开发的过程中稍微有点头绪。
how to write a plugin这个教程还是可以好好看看的,尤其是那个simple example,它会教你在compilation的emit事件或之前,将你需要生成的文件放到webpack的compilation.assets里,这样就可以借助webpack的力量帮你生成文件,而不需要自己手动去写fs.writeFileSync。
主要就是这段代码
?
1
2
3
4
5
6
7
8

compilation.assets[’filelist.md’] = {
source: function() {
return filelist;
},
size: function() {
return filelist.length;
}
};
基本特性介绍
首先,定义一个函数func,用户设置的options基本就在这里处理。
其次,需要设一个func.prototype.apply函数。这个函数是提供给webpack运行时调用的。webpack会在这里注入compiler对象。
输出complier对象,你会看到这一长串的内容,初步一看,我看出了两大类(有补充的可以告诉我)。一个webpack运行时的参数,例如_plugins,这些数组里的函数应该是webpack内置的函数,用于在compiltion,this-compilation和should-emit事件触发时调用的。另一个是用户写在webpack.config.js里的参数。隐约觉得这里好多未来都可能会是webpack暴露给用户的接口,使webpack的定制化功能更强大。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

Compiler {
_plugins:
{ compilation: [ [Function], [Function], [Function], [Function] ],
’this-compilation’: [ [Function: bound ] ],
’should-emit’: [ [Function] ] },
outputPath: ’’,
outputFileSystem: null,
inputFileSystem: null,
recordsInputPath: null,
recordsOutputPath: null,
records: {},
fileTimestamps: {},
contextTimestamps: {},
resolvers:
{ normal: Tapable { _plugins: {}, fileSystem: null },
loader: Tapable { _plugins: {}, fileSystem: null },
context: Tapable { _plugins: {}, fileSystem: null } },
parser:
Parser {
_plugins:
{ ’evaluate Literal’: [Object],
’evaluate LogicalExpression’: [Object],
’evaluate BinaryExpression’: [Object],
’evaluate UnaryExpression’: [Object],
’evaluate typeof undefined’: [Object],
’evaluate Identifier’: [Object],
’evaluate MemberExpression’: [Object],
’evaluate CallExpression’: [Object],
’evaluate CallExpression .replace’: [Object],
’evaluate CallExpression .substr’: [Object],
’evaluate CallExpression .substring’: [Object],
’evaluate CallExpression .split’: [Object],
’evaluate ConditionalExpression’: [Object],
’evaluate ArrayExpression’: [Object],
’expression Spinner’: [Object],
’expression ScreenMod’: [Object] },
options: undefined },
options:
{ entry:
{
’index’: ’/Users/mac/web/src/page/index/main.js’ },
output:
{ publicPath: ’/homework/features/model/’,
path: ’/Users/mac/web/dist’,
filename: ’js/[name].js’,
libraryTarget: ’var’,
sourceMapFilename: ’[file].map[query]’,
hotUpdateChunkFilename: ’[id].[hash].hot-update.js’,
hotUpdateMainFilename: ’[hash].hot-update.json’,
crossOriginLoading: false,
hashFunction: ’md5’,
hashDigest: ’hex’,
hashDigestLength: 20,
sourcePrefix: ’\t’,
devtoolLineToLine: false },
externals: { react: ’React’ },
module:
{ loaders: [Object],
unknownContextRequest: ’.’,
unknownContextRecursive: true,
unknownContextRegExp: /^\.\/.*$/,
unknownContextCritical: true,
exprContextRequest: ’.’,
exprContextRegExp: /^\.\/.*$/,
exprContextRecursive: true,
exprContextCritical: true,
wrappedContextRegExp: /.*/,
wrappedContextRecursive: true,
wrappedContextCritical: false },
resolve:
{ extensions: [Object],
alias: [Object],
fastUnsafe: ,
packageAlias: ’browser’,
modulesDirectories: [Object],
packageMains: [Object] },
plugins:
[ [Object],
[Object],
[Object],
[Object],
NoErrorsPlugin {},
[Object],
[Object] ],
devServer: { port: 8081, contentBase: ’./dist’ },
context: ’/Users/mac/web/’,
watch: true,
debug: false,
devtool: false,
cache: true,
target: ’web’,
node:
{ console: false,
process: true,
global: true,
setImmediate: true,
__filename: ’mock’,
__dirname: ’mock’ },
resolveLoader:
{ fastUnsafe: ,
alias: {},
modulesDirectories: [Object],
packageMains: [Object],
extensions: [Object],
moduleTemplates: [Object] },
optimize: { occurenceOrderPreferEntry: true } },
context: ’/Users/mac/web/’ }
除此以外,compiler还有一些如run, watch-run的方法以及compilation, normal-module-factory对象。我目前用到的,主要是compilation。其它的等下一篇有机会再说。
对比起compiler还有compiler.plugin函数。这个相当于是插件可以进行处理的webpack的运行中的一些任务点,webpack就是完成一个又一个任务而完成整个打包构建过程的。如make是最开始的起点, complie就是编译任务点,after-complie是编译完成,emit是即将准备生成文件,after-emit是生成文件之后等等,前面几个都是比较生动形象的任务点。
至于compilation,它继承于compiler,所以能拿到一切compiler的内容(所以你也会看到webpack的options),而且也有plugin函数来接入任务点。在compiler.plugin(’emit’)任务点输出compilation,会得到大致下面的对象数据,因为实在太长,我只保留了最重要的assets部份:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

assetsCompilation {
assets:
{ ’js/index/main.js’:
CachedSource {
_source: [Object],
_cachedSource: undefined,
_cachedSize: undefined,
_cachedMaps: {} } },
errors: ,
warnings: ,
children: ,
dependencyFactories:
ArrayMap {
keys:
[ [Object],
[Function: MultiEntryDependency],
[Function: SingleEntryDependency],
[Function: LoaderDependency],
[Object],
[Function: ContextElementDependency],
values:
[ NullFactory {},
[Object],
NullFactory {} ] },
dependencyTemplates:
ArrayMap {
keys:
[ [Object],
[Object],
[Object] ],
values:
[ ConstDependencyTemplate {},
RequireIncludeDependencyTemplate {},
NullDependencyTemplate {},
RequireEnsureDependencyTemplate {},
ModuleDependencyTemplateAsRequireId {},
AMDRequireDependencyTemplate {},
ModuleDependencyTemplateAsRequireId {},
AMDRequireArrayDependencyTemplate {},
ContextDependencyTemplateAsRequireCall {},
AMDRequireDependencyTemplate {},
LocalModuleDependencyTemplate {},
ModuleDependencyTemplateAsId {},
ContextDependencyTemplateAsRequireCall {},
ModuleDependencyTemplateAsId {},
ContextDependencyTemplateAsId {},
RequireResolveHeaderDependencyTemplate {},
RequireHeaderDependencyTemplate {} ] },
fileTimestamps: {},
contextTimestamps: {},
name: undefined,
_currentPluginApply: undefined,
fullHash: ’f4030c2aeb811dd6c345ea11a92f4f57’,
hash: ’f4030c2aeb811dd6c345’,
fileDependencies: [ ’/Users/mac/web/src/js/index/main.js’ ],
contextDependencies: ,
missingDependencies: }
assets部份重要是因为如果你想借助webpack帮你生成文件,你需要像官方教程how to write a plugin在assets上写上对应的文件信息。
除此以外,compilation.getStats()这个函数也相当重要,能得到生产文件以及chunkhash的一些信息,如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

assets{ errors: ,
warnings: ,
version: ’1.12.9’,
hash: ’5a5c71cb2accb8970bc3’,
publicPath: ’xxxxxxxxxx’,
assetsByChunkName: { ’index/main’: ’js/index/index-4c0c16.js’ },
assets:
[ { name: ’js/index/index-4c0c16.js’,
size: 453,
chunks: [Object],
chunkNames: [Object],
emitted: undefined } ],
chunks:
[ { id: 0,
rendered: true,
initial: true,
entry: true,
extraAsync: false,
size: 221,
names: [Object],
files: [Object],
hash: ’4c0c16e8af4d497b90ad’,
parents: ,
origins: [Object] } ],
modules:
[ { id: 0,
identifier: ’multi index/main’,
name: ’multi index/main’,
index: 0,
index2: 1,
size: 28,
cacheable: true,
built: true,
optional: false,
prefetched: false,
chunks: [Object],
assets: ,
issuer: null,
profile: undefined,
failed: false,
errors: 0,
warnings: 0,
reasons: },
{ id: 1,
identifier: ’/Users/mac/web/node_modules/babel-loader/index.js?presets=es2015⪯sets=react!/Users/mac/web/src/js/main/index.js’,
name: ’./src/js/index/main.js’,
index: 1,
index2: 0,
size: 193,
cacheable: true,
built: true,
optional: false,
prefetched: false,
chunks: [Object],
assets: ,
issuer: ’multi index/main’,
profile: undefined,
failed: false,
errors: 0,
warnings: 0,
reasons: [Object],
source: ’’ // 具体文件内容}
],
filteredModules: 0,
children: }
这里的chunks数组里,是对应会生成的文件,以及md5之后的文件名和路径,里面还有文件对应的chunkhash(每个文件不同,但如果你使用ExtractTextPlugin将css文件独立出来的话,它会与require它的js入口文件共享相同的chunkhash),而assets.hash则是统一的hash,对每个文件都一样。值得关注的是chunks里的每个文件,都有source这一项目,提供给开发者直接拿到源文件内容(主要是js,如果是css且使用ExtractTextPlugin,则请自行打印出来参考)。
例子
接下来,会以最近我写的一个插件 html-res-webpack-plugin 作为引子,来介绍基本的写插件原理。插件的逻辑就写在index.js里。
首先,将用户输入的参数在定好的函数中处理,HtmlResWebpackPlugin。
然后,新增apply函数,在里面写好插件需要切入的webpack任务点。目前HtmlResWebpackPlugin插件只用到emit这个任务点,其它几个仅作为演示。
第三步,调用addFileToWebpackAsset方法,写compilation.assets,借助webpack生成html文件。
第四步,在开发模式下(isWatch = true),直接生成html,但在生产模式下(isWatch = true),插件会开始对静态资源(js,css)进行md5或者内联。
第五步,调用findAssets方法是为了通过compilation.getStats()拿到的数据,去匹配对应的静态资源,还有找到对应的哈希(是chunkhash还是hash)。
最六步,调用addAssets方法,对静态资源分别做内联或者md5文件处理。内联资源的函数是inlineRes,你会看到我使用了compilation.assets[hashFile].source() 及 compilation.assets[hashFile].children._value。前者是针对于js的,后者是针对使用了ExtractTextPlugin的css资源。
最后一步,即是内联和md5完成后,再更新一下compilation.assets中对应生成html的source内容,才能正确地生成内联和md5后的内容。
后记
有兴趣可以试用一下 html-res-webpack-plugin 这个插件(为什么要写一个新的html生成插件,我在readme里写了,此处不赘述),看看有哪些用得不爽之处。目前只是第一版,还不适合用于生产环境。希望第二版的时候能适用于更多的场景,以及性能更好。到是,我也会写第二篇插件开发文章,将本文还没提到的地方一一补充完整。也欢迎大家在这里发贴,或者指出本人的谬误之处。
-前端

如何写一个webpack插件


前言
最近由于用着html-webpack-plugin觉得很不爽,于是乎想自己动手写一个插件。原以为像gulp插件一样半天上手一天写完,但令人郁闷的是完全找不到相关的文章。一进官方文档却是被吓傻了。首先是进入 how to write a plugin 看了一页简单的介绍。然后教程会告诉你,你需要去了解compiler和compilation这两个对象,才能更好地写webpack的插件,然后作者给了github的链接给你,让你去看源代码,我晕。不过幸好最后给了一个 plugins 的API文档,才让我开发的过程中稍微有点头绪。
how to write a plugin这个教程还是可以好好看看的,尤其是那个simple example,它会教你在compilation的emit事件或之前,将你需要生成的文件放到webpack的compilation.assets里,这样就可以借助webpack的力量帮你生成文件,而不需要自己手动去写fs.writeFileSync。
主要就是这段代码
?
1
2
3
4
5
6
7
8

compilation.assets[’filelist.md’] = {
source: function() {
return filelist;
},
size: function() {
return filelist.length;
}
};
基本特性介绍
首先,定义一个函数func,用户设置的options基本就在这里处理。
其次,需要设一个func.prototype.apply函数。这个函数是提供给webpack运行时调用的。webpack会在这里注入compiler对象。
输出complier对象,你会看到这一长串的内容,初步一看,我看出了两大类(有补充的可以告诉我)。一个webpack运行时的参数,例如_plugins,这些数组里的函数应该是webpack内置的函数,用于在compiltion,this-compilation和should-emit事件触发时调用的。另一个是用户写在webpack.config.js里的参数。隐约觉得这里好多未来都可能会是webpack暴露给用户的接口,使webpack的定制化功能更强大。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

Compiler {
_plugins:
{ compilation: [ [Function], [Function], [Function], [Function] ],
’this-compilation’: [ [Function: bound ] ],
’should-emit’: [ [Function] ] },
outputPath: ’’,
outputFileSystem: null,
inputFileSystem: null,
recordsInputPath: null,
recordsOutputPath: null,
records: {},
fileTimestamps: {},
contextTimestamps: {},
resolvers:
{ normal: Tapable { _plugins: {}, fileSystem: null },
loader: Tapable { _plugins: {}, fileSystem: null },
context: Tapable { _plugins: {}, fileSystem: null } },
parser:
Parser {
_plugins:
{ ’evaluate Literal’: [Object],
’evaluate LogicalExpression’: [Object],
’evaluate BinaryExpression’: [Object],
’evaluate UnaryExpression’: [Object],
’evaluate typeof undefined’: [Object],
’evaluate Identifier’: [Object],
’evaluate MemberExpression’: [Object],
’evaluate CallExpression’: [Object],
’evaluate CallExpression .replace’: [Object],
’evaluate CallExpression .substr’: [Object],
’evaluate CallExpression .substring’: [Object],
’evaluate CallExpression .split’: [Object],
’evaluate ConditionalExpression’: [Object],
’evaluate ArrayExpression’: [Object],
’expression Spinner’: [Object],
’expression ScreenMod’: [Object] },
options: undefined },
options:
{ entry:
{
’index’: ’/Users/mac/web/src/page/index/main.js’ },
output:
{ publicPath: ’/homework/features/model/’,
path: ’/Users/mac/web/dist’,
filename: ’js/[name].js’,
libraryTarget: ’var’,
sourceMapFilename: ’[file].map[query]’,
hotUpdateChunkFilename: ’[id].[hash].hot-update.js’,
hotUpdateMainFilename: ’[hash].hot-update.json’,
crossOriginLoading: false,
hashFunction: ’md5’,
hashDigest: ’hex’,
hashDigestLength: 20,
sourcePrefix: ’\t’,
devtoolLineToLine: false },
externals: { react: ’React’ },
module:
{ loaders: [Object],
unknownContextRequest: ’.’,
unknownContextRecursive: true,
unknownContextRegExp: /^\.\/.*$/,
unknownContextCritical: true,
exprContextRequest: ’.’,
exprContextRegExp: /^\.\/.*$/,
exprContextRecursive: true,
exprContextCritical: true,
wrappedContextRegExp: /.*/,
wrappedContextRecursive: true,
wrappedContextCritical: false },
resolve:
{ extensions: [Object],
alias: [Object],
fastUnsafe: ,
packageAlias: ’browser’,
modulesDirectories: [Object],
packageMains: [Object] },
plugins:
[ [Object],
[Object],
[Object],
[Object],
NoErrorsPlugin {},
[Object],
[Object] ],
devServer: { port: 8081, contentBase: ’./dist’ },
context: ’/Users/mac/web/’,
watch: true,
debug: false,
devtool: false,
cache: true,
target: ’web’,
node:
{ console: false,
process: true,
global: true,
setImmediate: true,
__filename: ’mock’,
__dirname: ’mock’ },
resolveLoader:
{ fastUnsafe: ,
alias: {},
modulesDirectories: [Object],
packageMains: [Object],
extensions: [Object],
moduleTemplates: [Object] },
optimize: { occurenceOrderPreferEntry: true } },
context: ’/Users/mac/web/’ }
除此以外,compiler还有一些如run, watch-run的方法以及compilation, normal-module-factory对象。我目前用到的,主要是compilation。其它的等下一篇有机会再说。
对比起compiler还有compiler.plugin函数。这个相当于是插件可以进行处理的webpack的运行中的一些任务点,webpack就是完成一个又一个任务而完成整个打包构建过程的。如make是最开始的起点, complie就是编译任务点,after-complie是编译完成,emit是即将准备生成文件,after-emit是生成文件之后等等,前面几个都是比较生动形象的任务点。
至于compilation,它继承于compiler,所以能拿到一切compiler的内容(所以你也会看到webpack的options),而且也有plugin函数来接入任务点。在compiler.plugin(’emit’)任务点输出compilation,会得到大致下面的对象数据,因为实在太长,我只保留了最重要的assets部份:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

assetsCompilation {
assets:
{ ’js/index/main.js’:
CachedSource {
_source: [Object],
_cachedSource: undefined,
_cachedSize: undefined,
_cachedMaps: {} } },
errors: ,
warnings: ,
children: ,
dependencyFactories:
ArrayMap {
keys:
[ [Object],
[Function: MultiEntryDependency],
[Function: SingleEntryDependency],
[Function: LoaderDependency],
[Object],
[Function: ContextElementDependency],
values:
[ NullFactory {},
[Object],
NullFactory {} ] },
dependencyTemplates:
ArrayMap {
keys:
[ [Object],
[Object],
[Object] ],
values:
[ ConstDependencyTemplate {},
RequireIncludeDependencyTemplate {},
NullDependencyTemplate {},
RequireEnsureDependencyTemplate {},
ModuleDependencyTemplateAsRequireId {},
AMDRequireDependencyTemplate {},
ModuleDependencyTemplateAsRequireId {},
AMDRequireArrayDependencyTemplate {},
ContextDependencyTemplateAsRequireCall {},
AMDRequireDependencyTemplate {},
LocalModuleDependencyTemplate {},
ModuleDependencyTemplateAsId {},
ContextDependencyTemplateAsRequireCall {},
ModuleDependencyTemplateAsId {},
ContextDependencyTemplateAsId {},
RequireResolveHeaderDependencyTemplate {},
RequireHeaderDependencyTemplate {} ] },
fileTimestamps: {},
contextTimestamps: {},
name: undefined,
_currentPluginApply: undefined,
fullHash: ’f4030c2aeb811dd6c345ea11a92f4f57’,
hash: ’f4030c2aeb811dd6c345’,
fileDependencies: [ ’/Users/mac/web/src/js/index/main.js’ ],
contextDependencies: ,
missingDependencies: }
assets部份重要是因为如果你想借助webpack帮你生成文件,你需要像官方教程how to write a plugin在assets上写上对应的文件信息。
除此以外,compilation.getStats()这个函数也相当重要,能得到生产文件以及chunkhash的一些信息,如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

assets{ errors: ,
warnings: ,
version: ’1.12.9’,
hash: ’5a5c71cb2accb8970bc3’,
publicPath: ’xxxxxxxxxx’,
assetsByChunkName: { ’index/main’: ’js/index/index-4c0c16.js’ },
assets:
[ { name: ’js/index/index-4c0c16.js’,
size: 453,
chunks: [Object],
chunkNames: [Object],
emitted: undefined } ],
chunks:
[ { id: 0,
rendered: true,
initial: true,
entry: true,
extraAsync: false,
size: 221,
names: [Object],
files: [Object],
hash: ’4c0c16e8af4d497b90ad’,
parents: ,
origins: [Object] } ],
modules:
[ { id: 0,
identifier: ’multi index/main’,
name: ’multi index/main’,
index: 0,
index2: 1,
size: 28,
cacheable: true,
built: true,
optional: false,
prefetched: false,
chunks: [Object],
assets: ,
issuer: null,
profile: undefined,
failed: false,
errors: 0,
warnings: 0,
reasons: },
{ id: 1,
identifier: ’/Users/mac/web/node_modules/babel-loader/index.js?presets=es2015⪯sets=react!/Users/mac/web/src/js/main/index.js’,
name: ’./src/js/index/main.js’,
index: 1,
index2: 0,
size: 193,
cacheable: true,
built: true,
optional: false,
prefetched: false,
chunks: [Object],
assets: ,
issuer: ’multi index/main’,
profile: undefined,
failed: false,
errors: 0,
warnings: 0,
reasons: [Object],
source: ’’ // 具体文件内容}
],
filteredModules: 0,
children: }
这里的chunks数组里,是对应会生成的文件,以及md5之后的文件名和路径,里面还有文件对应的chunkhash(每个文件不同,但如果你使用ExtractTextPlugin将css文件独立出来的话,它会与require它的js入口文件共享相同的chunkhash),而assets.hash则是统一的hash,对每个文件都一样。值得关注的是chunks里的每个文件,都有source这一项目,提供给开发者直接拿到源文件内容(主要是js,如果是css且使用ExtractTextPlugin,则请自行打印出来参考)。
例子
接下来,会以最近我写的一个插件 html-res-webpack-plugin 作为引子,来介绍基本的写插件原理。插件的逻辑就写在index.js里。
首先,将用户输入的参数在定好的函数中处理,HtmlResWebpackPlugin。
然后,新增apply函数,在里面写好插件需要切入的webpack任务点。目前HtmlResWebpackPlugin插件只用到emit这个任务点,其它几个仅作为演示。
第三步,调用addFileToWebpackAsset方法,写compilation.assets,借助webpack生成html文件。
第四步,在开发模式下(isWatch = true),直接生成html,但在生产模式下(isWatch = true),插件会开始对静态资源(js,css)进行md5或者内联。
第五步,调用findAssets方法是为了通过compilation.getStats()拿到的数据,去匹配对应的静态资源,还有找到对应的哈希(是chunkhash还是hash)。
最六步,调用addAssets方法,对静态资源分别做内联或者md5文件处理。内联资源的函数是inlineRes,你会看到我使用了compilation.assets[hashFile].source() 及 compilation.assets[hashFile].children._value。前者是针对于js的,后者是针对使用了ExtractTextPlugin的css资源。
最后一步,即是内联和md5完成后,再更新一下compilation.assets中对应生成html的source内容,才能正确地生成内联和md5后的内容。
后记
有兴趣可以试用一下 html-res-webpack-plugin 这个插件(为什么要写一个新的html生成插件,我在readme里写了,此处不赘述),看看有哪些用得不爽之处。目前只是第一版,还不适合用于生产环境。希望第二版的时候能适用于更多的场景,以及性能更好。到是,我也会写第二篇插件开发文章,将本文还没提到的地方一一补充完整。也欢迎大家在这里发贴,或者指出本人的谬误之处。
-webpack原理

vue2.0用脚手架搭建的官方例子怎么用webpack打包 如何配置


前言 vue2 然后通过以下命令安装 webpack cnpm install webpack -g 注:下面 orange 默认给出 npm 的安装方案,安装失败请自行转为 cnpm 安装 在需要创建工程的位置运行 vue init webpack-simple 工程名字《工程名字不能用中文》 或者创建 vue1.0 的项目,只需将命令换成 vue init webpack-simple#1.0 这里我们基于 2.x 开发的,直接使用第一种方法创建工程即可,下图是创建工程时的截图,需要你添加 Project name,Project description,Author. 图中已经给出下一步应该操作的步骤,我们按照步骤一步一步执行,这里 orange 不给大家一步一步列出。 注意:这里一定要使用 npm install 安装官方库,而不要使用淘宝镜像,会导致部分依赖丢失。 安装完成后,目录如下图。 然后我们运行我们的项目后浏览器会自动弹出,并展示以下页面 这里注意观察,默认给我们八个链接,可以根据这几个链接获得我们想要的学习资源,上面是必要的的链接(官方文档以及关注 vue 动态),下面是 vue 的生态系统,大家亲切的叫它们为全家桶。 二、Vue 全家桶 我们接下来介绍全家桶的安装(使用详情大家可以去初始页面的链接查看) 一句命令搞定全家桶 npm install vue-router vue-resource vuex --save package.json 已经加入了我们的全家桶,node_modules 目录下也有对应的依赖包,注意这里现在还不能用扩展之后的方法,因为我们没引入到项目中来。 src/main.js 修改如下 import Vue from ’vue’ import VueResource from ’vue-resource’ import VueRouter from ’vue-router’ import Vuex from ’vuex’ import App from ’./App.vue’ Vue.use(VueResource) Vue.use(VueRouter) Vue.use(Vuex) new Vue({ el: ’#app’, render: h =》 h(App) }) 这时我们的项目就能运行对应的扩展方法了 三、集成 Sass 作为移动端的开发怎么能缺少 css 预编译语言。sass 安装需要几个依赖。 我们干脆在 package.json 把版本写死,然后通过 npm install 安装 在 “devDependencies“: {} 中添加下面几个依赖 “node-sass“: “^3.8.0“, “sass“: “^0.5.0“, “sass-loader“: “^4.0.0“, 好,我们 npm install 后,就可以正式使用 sass 啦 四、目录结构建议 依赖的安装到这里差不多结束了,其它大家需要的可以自定义安装 下面给出我的目录建议供大家参考, 这里的 img 目录放置图片,script 目录放置公共的工具函数,style 目录放置我们的 sass 文件, 你查看 App.vue 文件时不难发现,默认的把样式文件给到了模块里,这样样式一直跟着模块 orange 建议大家不要这样做,因为这样十分不利于样式的模块化,注意区分与模版模块化的区别, 我们单独设置 style 目录,并在目录当中对 sass 进行模块化处理(通过 import 引入 sass 模块) 对应的 App.vue 也变得非常简洁,代码如下 《style lang=“sass“》 @import “/style/base.scss“; 《/style》 五、rem 适配 对于移动端的开发,rem 适配必不可少,我们可以用多种方式实现,下面给出一种方案 在 index.html 中添加如下代码 《script》 let html = document.documentElement; window.rem = html.getBoundingClientRect().width / 16 ; html.style.fontSize = window.rem + ’px’; 《/script》 这里基于宽 320px 的屏幕分成了 16 份,也就是 1rem = 20px,目前大多数设计稿都是根据 iphone6 的宽( 375px )走的,建议大家在这里分成 25 份,也就是 1rem = 15px,计算起来方便些。 简单说下 rem 原理:根据 html 的 fontSize 属性值为基准,其它所有的 rem 值,根据这个基准计算。 我们根据屏幕宽度用 js 动态修改了 html 的 fontSize 属性值,达到移动端适配的目的 总结 以上就是这篇文章的全部内容了,本文作为移动端配置的基础篇,深入了解框架后才能继续构建网站,希望这是一个好的开始,有了这个架子再填充代码就方便了许多,不用再去考虑开发环境问题了。希望本文的内容对有需要的朋友们能有所帮助。
-前端

fis3和webpack有什么区别


fis/fis3是grunt、gulp之后兴起的一个比较优秀的前端工程解决方案。它的本质是基于静态资源标记+动态解析静态资源表,在模板、js里边使用特殊的标记方法引用前端资源,构建的时候生成一张资源依赖表,浏览器或者后端模板语言在解析的过程中通过查表得到某个静态资源在不同环境下的引用路径,所以不管是纯前端渲染(标记方法已经转换成浏览器能识别的了)还是后端(php、node、java)渲染,都很容易支持到,这样可以做到非常精细化的控制资源的按需加载。可以说fis真正做到了静态资源动态按需加载。
再来说说webpack,其实本质上是静态打包,生成chunk需要手动配置entry,虽然可以使用CommonsChunkPlugin、dll等插件辅助提取公共代码,以及采用code split切割进行懒加载,但这个粒度十分难把握,配置起来也比较困难(fis是自动生成依赖关系和自动处理懒加载)。
单从原理上来讲,fis比webpack要先进好多好多。但是fis先进的理念也成为它的一个缺陷,就是自定义的一套标记语言,大量自定义的资源标记语法并不能被浏览器直接接解析或者被社区流行的工具(browserify/babel)转换,比如fis依赖自家定制的mod.js来解析`require()`函数(即定位资源)以及资源base64内嵌语法`__include()`等。这一点直接导致了fis无法和后来的强大的npm社区对接,大量的npm前端模块在fis用不了,笔者认为这是导致各公司在选择前端统一的构建工具标准时不考虑fis的最大原因吧。
fis的作者
曾经说过,webpack只差一步——生成支持静态资源表,就完美了,深以为然!目前,webpack好像有生成sourcemap的插件,但是缺少配套的解析sourcemap的工具,后端的模板引擎貌似也没有支持sourcemap。
webpack现有的懒加载手段不完美,但是基本够用,主要是它直接支持commonjs规范的require语法,再配置babel等还可以直接解析ES6语法(当然,fis也可以通过插件支持ES6的),加上可以直接使用npm模块这一点,依托npm社区强大红利来搭建公司级公共组件化规范。所以综合考虑,webpack目前是前端构建工具的首选。
-webpack原理