我相信,有不少的朋友对 webpack 都有或多或少的了解。网上也有了各种各样的文章,文章内作者也写出了不少自己对于 webpack 这个工具的理解。在我刚刚接触 webpack 的时候,老实说,网上大部分的文章我是看不懂的。。webpack 里面有很多名词,是没有接触和理解过模块化的同学都难以理解的。我感觉,学习任何一项新技术,要弄清楚为什么使用它,它是什么,它有什么用等概念,弄清楚这些概念之后,我相信,在日后的 webpack 学习中会达到事半功倍的效果。这篇文章,我会以最简单的方式,阐述什么是 webpack。当然,这是我个人对 webpack 的一些理解,也是在学习中总结。
另外,最好的学习 webpack 的资源是 webpack 的官网。传送门:
当然,如果你早已是 webpack 的实践者,对 webpack 认识足够深入,这篇文章不太适合您阅读。如果你是小白,那就可以开启 webpack 的探索之路了。
- 1. 将许多松散的模块按照依赖关系和规则打包成符合生产环境环境部署的前端资源。
- 2. 将按需加载的模块进行代码分割,等到实际需要的时候再异步加载。
- 3. 通过加载器loader的转换,所有的前端资源都可以看作是模块。比如说CommonJS模块,AMD模块,ES6模块,CSS,sass,json,图片等。
短短的几句话,就有太多让人难以明白的地方。
1. 什么是前端资源?
2. 什么是模块?
3. 什么是模块化?
4. 什么叫按需加载?
5. 如何实现按需加载?
6. 什么是 plugins?
7. 什么是 loader?
所谓前端资源,就是我们在创建 html 时,引入的 script,link,img,json 等文件。webpack 足够的优秀,只需要在 html 文件中引入一个 js 文件,在定义一个入口文件 js,用于存放依赖的模块,就可以将其他前端资源按照依赖关系和规则打包。
以前我们需要这样来引入文件。
- <link rel="stylesheet" href="style/stylesheets/screen.css" media='screen'
- />
- <script src='script/jquery-2.2.1.min.js'>
- </script>
- <script src='script/bootstrap.js'>
- </script>
- <script src="script/index.js">
- </script>
index.js 依赖 bootstrap.js,而 bootstrap 又依赖于 jquery。我们必须按照 DOM 顺序来写每一个 js 文件。
现在只需要在 html 中引入一个主文件 index.js,其他依赖的前端资源都写在另外一个入口文件 js 中,这个入口文件不用写在 html 中,然后配置好 config,在 cmd 中输入 webpack 执行编译,所有前端资源都被引入了。并且 webpack 会帮我们入口文件 entry.js 的每个模块的类型和依赖关系,等到需要的时候再按需加载。
- //html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Document</title>
- </head>
- <body>
- <script src='js/main.js'></script>
- <!-- 引入引入主模块 main.js -->
- </body>
- </html>
- //entry.js
- import $ from 'jquery';
- import other...
在 webpack 中,所有的前端资源都是模块,可以通过加载器 loader 进行转换。在 javascript 方面,有几大模块系统,CommonJS 模块,AMD 模块,CMD 模块,ES6 模块。谈谈我对这几大模块的理解。
在 CommonJS 中,有一个全局方法 require(),用于加载依赖模块。
- //main.js
- var jquery = require('jquery');
- var bootstrap = require('bootstrap');
主文件 main.js 模块依赖于这两个模块,CommonJS 缺点就是同步加载。也就是说,会先加载 jquery 模块,等到 jquery 加载完了再去加载 bootstrap 模块。引用阮一峰老师的话
假死意味着浏览器任然处在加载中,仍然还是空白页面。这种假死的状态带来的后果就是用户的离开。
AMD 也叫异步模块定义,英文 Asynchronous Module Definition。AMD 是 requirejs 对模块定义时的产出。它采用异步方式加载模块,每个独立模块的加载不影响回调函数中定义的模块的运行。在回调函数中定义一个模块,只有当依赖的模块加载完成之后,该模块才会编译执行。AMD 也采用 require() 语句来加载一个模块,但是不同于 CommonJS,它有两个参数。第一个参数是数组,需要传入依赖模块;第二个参数是回调函数,回调函数中也接受参数,而参数是形式参数,来自于每一个依赖模块。举个例子。
- //main.js
- require(['jquery', 'bootstrap'],
- function($, boot) {
- //写入的模块
- })
主模块 main.js 依赖于 jquery,Bootstrap 模块,main 只有在这两个模块加载完成之后才会编译执行 main 定义的模块,属于同步加载。而在两个依赖模块中,属于异步加载。也就是说 bootstrap 不用等到 jquery 加载完成之后再加载,被依赖的模块是哪一个模块小,就先加载哪一个,这就避免了 CommonJS 模块中同步加载依赖模块而出现浏览器假死的状态。稍微总结一下,
主模块需要等到依赖模块加载完成之后才编译执行,属于同步加载;而被依赖模块之间属于异步加载,哪一个模块小,就先加载哪一个。
AMD 模块的一大不足就是所有依赖的模块都需要提前加载,依赖前置。
CMD 模块跟 AMD 很相似,这里引用玉伯老师的话看看 CMD 和 AMD 的区别。
依赖就近的意思就是当我需要某个模块的时候再去异步加载。也就是按需要加载前端资源,懒加载。
可以用八个字来总结 CMD。依赖就近,延迟执行。
ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。ES6 中与 CommonJS,AMD 模块一个区别在于 ES6 是通过 import 关键字来输入某个模块提供的功能,export 或者 export default 规定该模块的对外接口,通俗易懂一些,也就是暴露该模块定义的一些属性和方法,供其他模块调用。
ES6 和 CommonJS,AMD 模块最大的区别在于,ES6 模块可以实现编译时加载,简单的说就是按需加载。而后两种方法只能是运行时加载。上一段代码。
- ES6:import {
- get,
- post,
- ajax
- }
- from 'jQuery';
- CommonJS:
- var
webpack 是支持以上的模块系统的。在头脑中要形成某种技术的知识框架才能学好该技术,所以花费了一些时间做了些介绍。模块化就是 webpack 使用某种方法将每一个松散的模块按照依赖关系编译的过程。webpack 需要一个入口 js 文件,主模块 js 文件,config 文件就可以实现前端资源模块化。看个简单的例子。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>
- Document
- </title>
- </head>
- <body>
- <script src='js/main.js'>
- </script>
- 1.
- <!-- 引入主模块 main.js -->
- </body>
- </html>
- 2. //入口文件 entry.js document.write("
- <h1>
- I'm Uncke Keith!
- </h1>
- "); 3. //webpack.config.js module.exports = { entry :'./app/js/entry.js',
- output : { path: './dist/js' filename: 'main.js' } }; 4. //打开cmd,在文件目录下,输入webpack执行编译就可以看到document.write()的结果了。
webpack 的其中一个作用就是可以将按需加载的模块进行代码分割,根据实际需要进行异步加载。按需加载,顾名思义就是按照用户需要某个功能的时候在加载相应的模块。举个例子,当我们在浏览一些图片网站的时候,如果图片在你打开该网站的时候就全部一起加载好,那造成的后果就是页面会保持一段时间的空白状态,直到全部图片加载完成之后才会显示。如果是按需要加载,就可以在用户刚进入页面的时候加载可视区域窗口的图片,当用户拖动滚动条下拉的时候再去加载图片,这样不仅减少 HTTP 请求,同时提高了页面加载速度。在举一个例子。在单页应用中,为了减少 http 请求次数,会把所有 js 文件合并为一个文件,这样请求数量减少了,可是请求的文件体积却变大了。按需加载就可以解决这个问题。
ES6 的一大涉及思想就是想让前端资源静态化,在编译的过程中,而非运行过程中就确定模块的依赖关系。webpack 在编译的过程中,就会对整个代码进行静态分析,分析出各个模块的类型和它们依赖关系,然后将不同类型的模块提交给适配的加载器来处理。比如一个用 Sass 写的样式模块,可以先用 Sass-Loader 加载器将它转成一个 CSS 模块,在通过 CSS 模块把他插入到页面的 style 标签中执行。并且在你编译的时候就确定了依赖关系。
webpack 跟我们提供了很多内置的插件,可以实现 loader 做不到的事情。在这里介绍几个常用的插件。
我们在入口文件中 import(或者 require)进一些 css 文件时,webpack 会帮我们把 css 样式与其他前端资源打包到 output 的 filename 文件中,然后在 head 标签中会自动加载一个 style 标签。但是,我们可能会需要独立出一个 css 文件,这时候就需要使用 extract-text-webpack-plugin 插件了。具体用法如下:
当我们成功生成了单独的 css 文件之后,就可以通过 link 标签引入了。
Webpack 本身只能处理原生的 JavaScript 模块,但是 loader 转换器可以将各种类型的资源转换成 JavaScript 模块。这样,任何资源都可以成为 Webpack 可以处理的模块。每一个 loader 都会针对入口文件 entry.js 的依赖模块引入的前端资源进行转换。站在更高的角度上看问题,我们在 webpack.config.js 里面的所有配置都是针对入口文件的,始终记住这点很重要。因为这就是我们配置 webpack.config.js 的目的。在这里介绍一些常用 loader。
我是一名 sass 实践者,并且用着一个封装了很多 mixins 的 compass 库,这个库的最大好处就是可以直接 include 一些 compass 封装好的的 css3 的 mixin 时,编译之后会帮我们加上 css 前缀。如果想在. vue 中也实现这种自动前缀的功能,可以使用 webpack 给我们提供的 postcss-loader。(可以去官网查看相关介绍)。postcss?这又是什么鬼,其实 postcss 只是一个平台,我们需要用的是基于 postcss 平台上的一些常用的插件。
如果想使用这个插件,需要下载一些依赖。
- cnpm install autoprefixer--save - dev
- //autoprefixer用于添加css3前缀
具体的 webpack.config.js 配置如下
- //注意先引入依赖
- var ExtractTextPlugin = require('extract-text-webpack-plugin');
- var autoprefixer = require('autoprefixer');
- module.exports = {
- entry: __dirname + '/app/entry.js',
- output: {
- path: __dirname + '/dist',
- filename: 'bundle.js'
- },
- module: {
- config...
- },
- postcss:[
- autoprefixer()
- ]
- };
如果你的入口文件 entry.js 是这样的。
- ...
- import './assets/main.scss';
那么当然你在 main.scss 使用 scss 语法写一些 css3 属性时,在编译之后就可以看到 css3 前缀了。但是如果你的样式是写在 *.vue 组件里面的
比如说,入口文件 entry.js 是这样的。
- ...
- import app from './app.vue';
topBar.vue
- <template>
- config...
- </template>
- <script>
- config...
- </script>
- <style lang='sass' scoped>
- .main{
- font-weight: bold;
- p {
- border-radius: 10px;
- box-shadow: 3px 3px 3px #ccc;
- transition: all 0.3s; 此处测试css3的属性前缀
- }
- }
- </style>
在浏览器中查看效果,你会发现,.vue 组件中定义的 style 样式并没有加上属性前缀。
- .main p[data-v-a64cfc10] {
- background-color: red;
- border-radius: 10px;
- box-shadow: 3px 3px 3px #ccc;
- color: red;
- display: inline-block;
- font-weight: bold;
- height: 100px;
- transition: all 0.3s ease 0s;
- }
难道是 postcss-loader 失效了?其实并不是。出现这种情况的原因主要还是对 vue-loader 不熟悉导致的。因为你是把样式写在了单个 *.vue 组件中,所以这里会涉及另外一个 lodaer,也就是 Vue 官方提供的 vue-loader。在 vue-loader 中如果也想让样式拥有前缀,在 webpack.config.js 要进行如下配置。
执行编译,你会发现正常显示了。
持续更新中..
来源: