CommonJS 和 AMD 是用于 JavaScript 模块管理的两大规范,前者定义的是模块的同步加载,主要用于 NodeJS ;而后者则是异步加载,通过 RequireJS 等工具适用于前端。随着 npm 成为主流的 JavaScript 组件发布平台,越来越多的前端项目也依赖于 npm 上的项目,或者自身就会发布到 npm 平台。因此,让前端项目更方便的使用 npm 上的资源成为一大需求。
web 开发中常用到的静态资源主要有 JavaScript、CSS、图片、Jade 等文件,webpack 中将静态资源文件称之为模块。 webpack 是一个 module bundler (模块打包工具),其可以兼容多种 js 书写规范,且可以处理模块间的依赖关系,具有更强大的 js 模块化的功能。Webpack 对它们进行统一的管理以及打包发布,其官方主页用下面这张图来说明 Webpack 的作用.
webpack 介绍
webpack 更 Gulp 的作用相同,是项目构建工具。
webpack 和 Gulp 的区别
Gulp 出现的比较早,更适合于做任务型的,可以处理任何的网站静态网站、SPA、Node.js 项目代码,Gulp 里面就是一堆的任务;
Webpack 一般全部用来处理 SPA 应用,就 React、Vue.js、AngularJS 使用。
所以使用的场景不一样,因为内部的原理不同。
webpack 官网文档
官网地址:http://webpack.github.io/docs/
webpack 的优势
对 CommonJS 、 AMD 、ES6 的语法做了兼容
对 js、css、图片等资源文件都支持打包
串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对 CoffeeScript、ES6的支持
有独立的配置文件 webpack.config.js
可以将代码切割成不同的 chunk,实现按需加载,降低了初始化时间
支持 SourceUrls 和 SourceMaps,易于调试
具有强大的 Plugin 接口,大多是内部插件,使用起来比较灵活
webpack 使用异步 IO 并具有多级缓存。这使得 webpack 很快且在增量编译上更加快
webpack 的使用
新建项目
在项目根目录下运行:
|
|
搭建基本的项目结构如下图:
src 中的开发文件,dist 是打包后的文件
安装
|
|
配置文件
webpack.develop.config.js
|
|
运行
|
|
进行版本控制
|
|
webpack 启动过程演进
把运行命令配置到 npm 的 script 中。 package.json
|
|
执行 :
|
|
更好的方式实现动启动
如果需要一直输入 npm run develop 确实是一件非常无聊的事情,我们可以把让他安静的运行,让我们设置 webpack-dev-server
除了提供模块打包功能,Webpack 还提供了一个基于 Node.js Express 框架的开发服务器,它是一个静态资源 Web 服务器,对于简单静态页面或者仅依赖于独立服务的前端页面,都可以直接使用这个开发服务器进行开 发。在开发过程中,开发服务器会监听每一个文件的变化,进行实时打包,并且可以推送通知前端页面代码发生了变化,从而可以实现页面的自动刷新。
更好的方式实现自动启动:webpack 官方提供的一个第三个的插件,自动监听代码变化,帮我们重新构建,把 webpack 和 express 封装了
|
|
调整 npm 的 package.json scripts 部分中开发命令的配置
|
|
webpack-dev-server - 在 localhost:8080 建立一个 Web 服务器
–devtool eval - 为你的代码创建源地址。当有任何报错的时候可以让你更加精确地定位到文件和行号
–progress - 显示合并代码进度
–colors – hot,命令行中显示颜色!
–content-base 指向设置的输出目录//这点一定是我们的发布目录
在 src 下面,新建一个 index.html 文件,
|
|
执行npm run develop
,结果如下图:
执行 npm run develop
之后我们发现执行没有结束,启动着监听,并在 8080 端口开启了一个服务器。
在浏览器中打开结果如下:
如果修改了 app.js 文件,会自动执行构建,刷新浏览器会发生变化。
在 index.html 访问的时候,会访问 bundle.js 文件,为什么,因为 webpack-dev-server 生成的 bundle 在内存中,放到内存中构建快
总的来说,当你运行 npm run develop 的时候,会启动一个 Web 服务器,然后监听文件修改,然后自动重新合并你的代码。真的非常简洁!
注意:
用 webpack-dev-server 生成 bundle.js 文件是在内存中的,并没有实际生成
如果引用的文件夹中已经有 bundle.js 就不会自动刷新了,你需要先把 bundle.js 文件手动删除
用 webstorm 需要注意,因为他是自动保存的,所以可能识别的比较慢,你需要手动的 ctrl+s 一下
浏览器自动刷新
修改 webpack.develop.config.js 的入口文件配置,修改 entry 部分如下:
|
|
修改了配置文件,重新启动,执行 npm run develop
结果如下图:
此时的目录结构如下图:
常用加载器
Loader:这是webpack准备的一些预处理工具
在构建项目之前做一些预处理操作,比如 ES6 转 ES5,Sass、Less
编译 JSX 和 ES6 到 ES5 语法的加载器
安装:
|
|
babel-loader: 转换器,编译 JSX 语法和 ES6 语法到 ES5 语法。
修改开发配置环境: webpack.develop.config.js
|
|
一个 React Hello, World! app.js 文件
|
|
加载 CSS
webpack 允许像加载任何代码一样加载 CSS。可以选择需要的方式,但是可以为每个组件把所有的 CSS 加载到入口主文件中来做任何事情。
加载 CSS 需要 css-loader 和 style-loader,他们做两件不同的事情:
css-loader 会遍历 CSS 文件,然后找到 url() 表达式然后处理他们
style-loader 会把原来的 CSS 代码插入页面中的一个 style 标签中
安装
|
|
新建文件夹:components
新增:_base.css Hello.css Hello.js Hello.sass 文件
修改配置文件:
|
|
!用来定义loader的串联关系,”-loader”是可以省略不写的,多个loader之间用“!”连接起来
Css加载策略
1、在主入口文件中,比如 src/app.js 你可以为整个项目加载所有的 CSS
|
|
CSS 就完全包含在合并的应用中,再也不需要重新下载。
2、懒加载(推荐)
如果想发挥应用中多重入口文件的优势,可以在每个入口点包含各自的 CSS。
把模块用文件夹分离,每个文件夹有各自的 CSS 和 JavaScript 文件。
再次,当应用发布的时候,导入的 CSS 已经加载到每个入口文件中。
3、定制组件css
可以根据这个策略为每个组件创建 CSS 文件,可以让组件名和 CSS 中的 class 使用一个命名空间,来避免一个组件中的一些 class 干扰到另外一些组件的 class。如下图:
4、使用内联样式取代 CSS 文件
在 “React Native” 中不再需要使用任何 CSS 文件,只需要使用 style 属性,可以把你的 CSS 定义成一个对象,那样就可以根据项目重新来考略你的 CSS 策略。
加载sass
下载依赖
|
|
修改配置文件
|
|
安装sass-loader之后运行运行 npm run develop
时报错如下:
解决:
|
|
图片处理
直到 HTTP/2 才能在应用加载的时候避免设置太多 HTTP 请求。
根据浏览器不同必须设置并行请求数,如果在 CSS 中加载了太多图片的话,可以自动把这些图片转成 BASE64 字符串然后内联到 CSS 里来降低必要的请求数,这个方法取决于图片大小。
需要为应用平衡下载的大小和下载的数量,不过 Webpack 可以让这个平衡十分轻松适应。
下载载依赖
|
|
修改配置文件:
|
|
加载器会把需要转换的路径变成 BASE64 字符串,在其他的 webpack 书中提到的这方面会把 CSS 中的 “url()” 像其他 require 或者 import 来处理。
意味着如果我们可以通过它来处理我们的图片文件。
url-loader 传入的 limit 参数是告诉它图片如果不大于 25KB 的话要自动在它从属的 css 文件中转成 BASE64 字符串。
大图片处理
在代码中是一下情况:
|
|
|
|
针对上面的两种使用方式,loader 可以自动识别并处理。根据 loader 中的设置,webpack 会将小于指点大小的文件转化成 base64 格式的 dataUrl,其他图片会做适当的压缩并存放在指定目录中。
这一步的目录如下:
components/Hello.js
|
|
components/_base.scss
|
|
components/Hello.css
|
|
components/Hello.scss
|
|
最终的 webpack.develop.config.js 文件
|
|
webpack 的部署策略
修改 npm 的 package.json 文件
“publish”: “ webpack –config webpack.publish.config.js -p”,
指向生产的配置文件,并且加上了webpack的cli的-p,他会自动做一些优化
分离应用和第三方
何时应该分离?
当应用依赖其他库尤其是像 React JS 这种大型库的时候,需要考虑把这些依赖分离出去,这样就能够让用户在更新应用之后不需要再次下载第三方文件。
当满足下面几个情况的时候你就需要这么做了:
1、当你的第三方的体积达到整个应用的 20% 或者更高的时候。
2、更新应用的时候只会更新很小的一部分
3、你没有那么关注初始加载时间,不过关注优化那些回访用户在你更新应用之后的体验。
4、有手机用户。
修改 webpack.publish.config.js 文件
|
|
可以看到,其实生产环境的配置和开发的配置没有太大的不同,主要是把不需要的东西给去掉了
|
|
|
|
注意:记住要把这些文件都加入到你的 HTML 代码中,但在上面这种引入后,在浏览器打开之后报下面这个错误,是因为引入顺序的问题
将上面 index.html 文件中的两个 js 文件引入顺序调换,如下
|
|
和 gulp 的集成
|
|
合并成单文件
一般情况下只有在下面的情况下才使用单入口模式:
1、应用很小
2、很少会更新应用
3、你不太关心初始加载时间
gulp + webpack 构建多页面前端项目
http://cnodejs.org/topic/56df76559386fbf86ddd6916
常用插件
压缩插件 webpack.optimize.UglifyJsPlugin, 这个插件是webpack自带的.
在配置文件中加入以下代码:
|
|
提取css插件
在 webpack 中编写 js 文件时,可以通过 require 的方式引入其他的静态资源,可通过 loade r对文件自动解析并打包文件。通常会将 js 文件打包合并,css文件会在页面的header中嵌入style的方式载入页面。但开发过程中我们并不想将样式打在脚本中,最好可以独立生成css文 件,以外链的形式加载。这时extract-text-webpack-plugin插件可以帮我们达到想要的效果。需要使用npm的方式加载插件,然后 参见下面的配置,就可以将js中的css文件提取,并以指定的文件名来进行加载。
|
|
只能把 css 抽出来,但是 sass 的样式不能分离出来。
|
|
自动创建 index.Html 页面插件
html-webpack-plugin
|
|
优化第三方包
|
|
自动打开浏览器插件
open-browser-webpack-plugin
https://github.com/baldore/open-browser-webpack-
webpack.develop.config.js
|
|
提取 js 公共部分插件
提取公共文件: CommonsChunkPlugin
|
|
ProvidePlugin插件
自动添加引用插件,全局暴露插件,直接使用
删除目录插件
clean-webpack-plugin
|
|
拷贝文件插件
copy-webpack-plugin
合并配置文件插件
webpack-config
https://github.com/mdreizin/webpack-config
最终的 webpack.publish.config.js 文件
|
|
最终的目录结构如下图:
开发阶段代码风格控制 eslint
安装:
|
|
|
|
.eslintrc.js 文件
|
|
其它知识点
webpack中的非入口文件(异步加载)
这个是重点要配合 chunkname 属性,react-router 的动态路由会用到
http://react-china.org/t/webpack-output-filename-output-chunkfilename/2256/2
基本上都是在 require.ensure 去加载模块的时候才会出现,chunkFileName,个人理解是 cmd 和 amd 异步加载而且没有给入口文件时,会生成了 no-name 的 chunk,所以 chunkFileName一般都会是 [id].[chunkhash].js, 也就是这种 chunk 的命名一般都会是 0.a5898fnub6.js.
Resolve属性
webpack 在构建包的时候会按目录的进行文件的查找,resolve 属性中的 extensions 数组中用于配置程序可以自行补全哪些文件后缀:
|
|
Externals属性
外部依赖不需要打包进 bundle,当我们想在项目中 require 一些其他的类库或者 API ,而又不想让这些类库的源码被构建到运行时文件中,这在实际开发中很有必要。
比如:在页面里通过 script 标签引用了 jQuery:<script src="//code.jquery.com/jquery-1.12.0.min.js"></script>
,所以并不想在其他 js 里再打包进入一遍,比如你的其他 js 代码类似:
其实就是不是通过require或者import引入的,而是直接写在html中的js地址。
|
|
这样用了 externals 属性时不用分离插件了,作用是这里引的插件不会被 webpack 所打包。要么用 cdn 要么需要 webpack 打包。下图为 webpack 中使用公用的 CDN:
开发环境中使用压缩文件
http://fakefish.github.io/react-webpack-cookbook/Optimizing-rebundling.html
不使用就会把 react 再处理一遍
noParse属性
module.noParse 是 webpack 的另一个很有用的配置项,如果确定一个模块中没有其他新的依赖项就可以配置这个像,webpack 将不再扫描这个文件中的依赖。
|
|
多文件入口
http://fakefish.github.io/react-webpack-cookbook/Multiple-entry-points.html
强制从新加载文件
http://fakefish.github.io/react-webpack-cookbook/Optimizing-caching.html
Chunk
代码分离:
http://webpack.github.io/docs/code-splitting.html
懒加载
(1)、在 react 中如何使用
http://fakefish.github.io/react-webpack-cookbook/Lazy-loaded-entry-points.html
(2)、在 react-router 中用到动态加载路由可以实现
在服务器端用 webpack
Node 和webpack 集成用到的中间件:http://www.tuicool.com/articles/IvQb2ey
Node 和webpack 集成过程中遇到的坑如何解决:http://www.tuicool.com/articles/zEZneuq
不推荐用 webpack 构建 Node 代码
热加载组件
http://fakefish.github.io/react-webpack-cookbook/Hot-loading-components.html
最后的 package.json
|
|