Webpack 简介
概述
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
Webpack 是当下最热门的前端资源模块化管理和打包工具,它可以将许多松散耦合的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分离,等到实际需要时再异步加载。通过 loader 转换,任何形式的资源都可以当做模块,比如 CommonsJS、AMD、ES6、CSS、JSON、CoffeeScript、LESS 等;
WebPack 是一款模块加载器兼打包工具,它能把各种资源,如 JS、JSX、ES6、SASS、LESS、图片等都作为模块来处理和使用。
Webpack是一个前端资源的打包工具,它可以将is、image、sss等资源当成一个模块进行打包(下图展示了所有的js打包成一个js,css打包成一个css等)
好处:
- 1、模块化开发
- 2、 编译typescript、ES6等高级js语法
- 3、CSS预编译
模块化的演进
Script 标签
<script src="module1.js"></scirpt> <script src="module2.js"></scirpt> <script src="module3.js"></scirpt> <script src="module4.js"></scirpt>
这是最原始的 JavaScript 文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在 window
对象中,不同模块的调用都是一个作用域。
这种原始的加载方式暴露了一些显而易见的弊端:
- 全局作用域下容易造成变量冲突
- 文件只能按照
<script>
的书写顺序进行加载 - 开发人员必须主观解决模块和代码库的依赖关系
- 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪
CommonsJS
服务器端的 NodeJS 遵循 CommonsJS 规范,该规范核心思想是允许模块通过 require
方法来同步加载所需依赖的其它模块,然后通过 exports
或 module.exports
来导出需要暴露的接口。
require("module"); var test = require("../module.js"); test.test(); export.doStuff = function() {}; module.exports = someValue;
优点
- 服务器端模块便于重用
- NPM 中已经有超过 45 万个可以使用的模块包
- 简单易用
缺点
- 同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
- 不能非阻塞的并行加载多个模块
实现
- 服务端的 NodeJS
- Browserify,浏览器端的 CommonsJS 实现,可以使用 NPM 的模块,但是编译打包后的文件体积较大
- modules-webmake,类似 Browserify,但不如 Browserify 灵活
- wreq,Browserify 的前身
AMD
Asynchronous Module Definition 规范其实主要一个主要接口 define(id?, dependencies?, factory);
它要在声明模块的时候指定所有的依赖 dependencies
,并且还要当做形参传到 factory
中,对于依赖的模块提前执行。
Asynchronous Module Definition 规范其实主要一个主要接口 define(id?, dependencies?, factory);
它要在声明模块的时候指定所有的依赖 dependencies
,并且还要当做形参传到 factory
中,对于依赖的模块提前执行。
define("module", ["dep1", "dep2"], function(d1, d2) { return someExportedValue; }); require(["module", "../file.js"], function(module, file) {});
优点
- 适合在浏览器环境中异步加载模块
- 可以并行加载多个模块
缺点
- 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不畅
- 不符合通用的模块化思维方式,是一种妥协的实现
实现
- RequireJS
- curl
CMD
Commons Module Definition 规范和 AMD 很相似,尽量保持简单,并与 CommonsJS 和 NodeJS 的 Modules 规范保持了很大的兼容性。
define(function(require, exports, module) { var $ = require("jquery"); var Spinning = require("./spinning"); exports.doSomething = ...; module.exports = ...; });
优点
- 依赖就近,延迟执行
- 可以很容易在 NodeJS 中运行
缺点
- 依赖 SPM 打包,模块的加载逻辑偏重
实现
- Sea.js
- coolie
ES6 模块
EcmaScript6 标准增加了 JavaScript 语言层面的模块体系定义。 ES6 模块的设计思想,是尽量静态化,使编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonsJS 和 AMD 模块,都只能在运行时确定这些东西。
import "jquery"; export function doStuff() {} module "localModule" {}
优点
- 容易进行静态分析
- 面向未来的 EcmaScript 标准
缺点
- 原生浏览器端还没有实现该标准
- 全新的命令,新版的 NodeJS 才支持
实现
- Babel
期望的模块系统
可以兼容多种模块风格,尽量可以利用已有的代码,不仅仅只是 JavaScript 模块化,还有 CSS、图片、字体等资源也需要模块化。
用webpack实现
安装webpack
安装Node.js
node.js概述
Node.js是一个Javascript运行环境(runtime environment),发布于2009年5月,由Ryan Dah开发,实质是对Chrome V8引擎进行了封装。Node js对一些特殊用例进行优化,提供替代的API,使得V8在非浏览器环境下运行得更好。
V8引擎执行Javascript的速度非常快,性能非常好。Nodejs是一个基于Chrome JavaScript运行时建立的平台,用于方便地搭建响应速度快、易于扩展的网络应用。Nodejs使用事件驱动,非阻塞/O模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用。
安装node.js
安装NPM
npm config set prefix "D: odejs pm_modules" npm config set cache "D: odejs pm_cache"
将D: odejs pm_modules添加到环境变量中
安装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
查看cnpm版本
cnpm -v
安装nrm
切换cnpm镜像地址(执行下面命令需要去下载好的nrm目录,如果没有添加环境变量)
cnpm install -g nrm //安装 nrm ls //查询当前指向的镜像地址 nrm use taobao //切换到淘宝镜像
安装webpack
- 本地安装
- 全局安装加-g,如下:
npm install webpack -g npm install webpack-cli -g
webpack入门程序
配置
创建 webpack.config.js
配置文件
- entry:入口文件,指定 WebPack 用哪个文件作为项目的入口
- output:输出,指定 WebPack 把处理完成的文件放置到指定路径
- module:模块,用于处理各种类型的文件
- plugins:插件,如:热更新、代码重用等
- resolve:设置路径指向
- watch:监听,用于设置文件改动后直接打包
module.exports = { entry: "", //指定入口文件(main.js) output: { path: "", //需要是绝对路径,默认是./dist/filename.js filename: "" }, module: { loaders: [ {test: /.js$/, loader: ""} ] }, plugins: {}, resolve: {}, watch: true }
执行
直接运行 webpack
命令打包
使用 WebPack
示例
- 创建项目
- 创建一个名为
modules
的目录,用于放置 JS 模块等资源文件 - 创建模块文件,如
hello.js
,用于编写 JS 模块相关代码 - 创建一个名为
main.js
的入口文件,用于打包时设置entry
属性 - 创建
webpack.config.js
配置文件,使用webpack
命令打包 - 创建 HTML 页面,如
index.html
,导入 WebPack 打包后的 JS 文件 - 运行 HTML 看效果
hello.js
需要将某些被其他js需要的方法导出(exports是ES5语法)
exports.sayHi = function () { document.write("<div>Hello WebPack</div>"); };
如果需要将多个方法导出
var function01 = funciotn(){}; var function02 = function(){}; exports.function01 = function01; 方式1:直接一个一个的导出 exports.function02 = function02; 方式2: exports = {function01,function02}
main.js内容
设置它为入口
导入需要的js
var hello = require("./hello"); //导入hellow.js(可以不加后缀) hello.sayHi();
webpack.config.js
的配置文件
主要目的:将上面的两个js打包成一个js,由于main.js依赖了hellow.js,所以他们会打包在一块
module.exports = { entry: "./modules/main.js", output: { filename: "./js/bundle.js" } };
HTML
index.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script src="dist/js/bundle.js"></script> </body> </html>
打包
# 用于监听变化 webpack --watch //在webpack.config.js 中的配置可以省略watch=true配置
运行
运行 HTML 文件,你会在浏览器看到:
Hello WebPack
webpack实战(手动打包)
model01.js
var add = function (x, y) { return x+y; } var add2 = function (x, y) { return x+y+2; } module.exports.add = add; // module.exports ={add,add2};//如果有多个方法这样导出 // module.exports.add2 = add2//如果有多个方法也可以这样导出
main.js(主文件)
//导入model01.js var {add} = require("./model01.js") var Vue = require("./vue.min.js") var VM = new Vue({ el:'#app',//vm接管了app区域的管理 //model数据 data:{ num1:0, num2:0 } });
webpack.config.js(一定需要添加不然报错)
module.exports = { entry: "./main.js", output: { filename: "build.js" } };
打包命令
webpack
效果
webpack(自动打包)
build.js并没有真正生成;
推荐使用webpack-dev-server开发服务器,它的功能可以实现热加载并且,自动刷新浏览器。
此时我们在开发中就可以不再使用nginx做服务器了
安装webpack-dev-server
cnpm install webpack@3.6.0 webpack-dev-server@2.9.1 html-webpack-plugin@2.30.1 --save-dev
配置webpack-dev-server
npm init
"scripts": { "dev": "webpack-dev-server --inline --hot --open --port 5008" },
- --inline:自动刷新
- --hot:热加载
- --port:指定端口
- --open:自动在默认浏览器打开
- --host:可以指定服务器的 ip,不指定则为127.0.0.1,如果对外发布则填写公网ip地址
·
配置webpack.config.js
//引用html-webpack-plugin插件,作用是根据html模板(vue_02.html)在内存生成html文件,它的工作原理是根据模板文件在内存中生成一个index.html文件。 var htmlwp = require('html-webpack-plugin'); module.exports={ entry:'./src/main.js', //指定打包的入口文件 output:{ path: __dirname+'/dist', // 注意:__dirname表示webpack.config.js(当前文件)所在目录的绝对路径(默认也是这个路径) filename:'build.js' //输出文件(没有真正的生成) }, //devtool: 'eval-source-map', plugins:[ new htmlwp({ title: '首页', //生成的页面标题<head><title>首页</title></head> filename: 'index.html', //webpack-dev-server在内存中生成的文件名称,自动将build注入到这个页面底部,才能实现自动刷新功能 template: 'vue_02.html' //根据vue_02.html这个模板来生成(这个文件请程序员自己生成) }) ] }
启动webpack-dev-server
补充文件
vue_02.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue.js常用指令的测试</title> </head> <body> <div id="app"> <input type="text" v-model="num1"/> + <input type="text" v-model="num2"/>= <span v-text="Number.parseInt(num1)+Number.parseInt(num2)"></span> <span v-text="result"></span> <button v-on:click="change">计算</button> </div> </body> <!--<script src="vue.min.js"></script>--> <!--<script src="dist/build.js"></script>--> </html>
main.js
var {add} = require("./model01") var Vue = require("./vue.min") var VM = new Vue({ el:'#app',//vm接管了app区域的管理 data:{//model数据 num1:0, num2:0, result:0 }, methods:{ change:function () { //必须要有this(会修改data中的result数据) this.result = add(Number.parseInt(this.num1),Number.parseInt(this.num2)) } } });
model01.js
var add = function (x, y) { return x+y; } var add2 = function (x, y) { return x+y+2; } module.exports.add = add; // module.exports ={add,add2};//如果有多个方法这样导出 // module.exports.add2 = add2//如果有多个方法也可以这样导出
webpack debug(调试)
1、在webpack.config.js中添加 devtool: 'eval-source-map',
2、在需要打上断点的js中添加debugger
methods:{ change:function () { debugger //调试 this.result = add(Number.parseInt(this.num1),Number.parseInt(this.num2)) } }
效果