webpack入坑之旅

博客链接:http://guowenfh.github.io/2016/03/24/vue-webpack-01-base/

项目初始化

1
2
3
4
$ npm i webpack -g
$ mkdir vue_by_webpack_blog && cd vue_by_webpack_blog
$ npm init
$ npm i webpack -D # --save-dev

目录

1
2
3
4
5
6
.
├── bundle.js
├── entry.js
├── index.html
├── node_modules
└── package.json

1 最简单的开始

不使用 webpack 配置文件

目录

1
2
3
4
5
6
7
8
.
├── bundle.js
├── entry.js #1
├── first.js #2
├── index.html #3
├── index.html
├── node_modules
└── package.json

#1 entry.js

1
2
3
4
/** entry.js **/
document.getElementById('app').innerHTML = "第一个成功打包的程序";

require('./first.js');

# 2 first.js

1
2
3
var h2 = document.createElement('h2');
h2.innerHTML = '不是吧,那么快就第二个打包程序了';
document.body.appendChild(h2);

#3 index.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1 id="app"></h1>
<script src="bundle.js"></script>
<!-- 引入 webpack 生成的文件 -->
</body>
</html>

1
2
3
4
5
6
7
8
9
$ webpack entry.js bundle.js # 打包

Hash: bd78725bb04725a3c1f9
Version: webpack 1.13.1
Time: 88ms
Asset Size Chunks Chunk Names
bundle.js 1.67 kB 0 [emitted] main
[0] ./entry.js 97 bytes {0} [built]
[1] ./first.js 104 bytes {0} [built]

Alt text

2 loader

2.1 处理css

目录

1
2
3
4
5
6
7
8
.
├── bundle.js
├── entry.js # 1
├── first.js
├── index.html
├── node_modules
├── package.json
└── style.css # 2
1
2
$ npm i css-loader -D # 读取 css 的 loader
$ npm i style-loader -D # 将 css 插入页面的 loader

# 1 entry.js

1
2
...
require('!style!css!./style.css');// 需要 !style!css! 前缀

#2 style.css

1
2
3
bady {
background-color: red;
}

1
$ webpack entry.js bundle.js

Alt text

2.2 根据扩展名自动绑定 loader

entry.js中的

1
require('!style!css!./style.css')

可以省略前缀!style!css!,改为:

1
require('./style.css')

但相应的打包命令就变为

1
$ webpack ./entry.js bundle.js --module-bind "css=style\!css"

3 webpack.config

目录

1
2
3
4
5
6
7
8
9
.
├── bundle.js
├── entry.js
├── first.js
├── index.html
├── node_modules
├── package.json
├── style.css
└── webpack.config.js # 1

#1 webpack.config.js

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
var Webpack = require('webpack');// 必须引入
module.exports = {
/**
* 指入口文件的配置项,它是一个数组的原因是webpack允许多个入口点。 当然如果你只有一个入口的话,也可以直接使用双引号"./entry.js"
* @type {Array}
*/

entry: [
"./entry.js"
],

/**
* 置打包结果,path定义了输出的文件夹,filename则定义了打包结果文件的名称
* @type {Object}
*/

output: {
path: __dirname,
filename: 'bundle.js'
},

/**
* 定义了对模块的处理逻辑,这里可以用loaders定义了一系列的加载器,以及一些正则。当需要加载的文件匹配test的正则时,就会调用后面的loader对文件进行处理
* @type {Object}
*/

module: {
loaders: [
/* 针对所有的 css 后缀的文件,使用 css-loader 读取,并使用 style-loader 插入到页面 */
{
test: /\.css$/,
loader: 'style!css'
}
]
},

/**
* 插件可以完成更多loader不能完成的功能
* @type {Array}
*/

plugins: [
new Webpack.BannerPlugin("这里是打包文件头部注释!")
]
}

1
2
3
4
5
6
7
8
9
10
$ webpack # 会自动在当前目录中查找webpack.config.js的配置文件
Hash: 4bd595f27c8112d7d21f
Version: webpack 1.13.1
Time: 2059ms
Asset Size Chunks Chunk Names
bundle.js 12 kB 0 [emitted] main
[0] multi main 28 bytes {0} [built]
[1] ./entry.js 133 bytes {0} [built]
[2] ./first.js 104 bytes {0} [built]
+ 4 hidden modules

4 高级

4.1 图片

1
2
$ npm install file-loader -D
$ npm install url-loader -D # 用来处理图片

目录

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── bundle.js
├── entry.js
├── first.js
├── img
│ ├── big.png
│ └── small.png
├── index.html # 1
├── node_modules
├── package.json
├── selectarea.mov
├── style.css # 2
└── webpack.config.js # 3

#1 index.html

1
2
3
4
5
...
<img src="img/small.png" alt="">
<div id="big"></div>
<div id="small"></div>
...

#2 style.css

1
2
3
4
5
6
7
...
#big {
background-image: url(img/big.png);
}
#small {
background-image: url(img/small.png);
}

#3 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var Webpack = require('webpack');// 必须引入
module.exports = {
...

module: {
loaders: [
...
/* 图片处理 */
{
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=8192'// 设置limit,小于8K的图片图片转化成了base64的编码,而大于8k的图片则不会转化
}
]
},
...
}

1
$ webpack

4.2 webpack命令选项

1
2
3
4
5
6
7
webpack #最基本的启动webpack命令
webpack -w #提供watch方法,实时进行打包更新
webpack -p #对打包后的文件进行压缩
webpack -d #提供SourceMaps,方便调试
webpack --colors #输出结果带彩色,比如:会用红色显示耗时较长的步骤
webpack --profile #输出性能数据,可以看到每一步的耗时
webpack --display-modules #默认情况下 node_modules 下的模块会被隐藏,加上这个参数可以显示这些被隐藏的模块

4..2.1 watch(热加载)

1
$ webpack --watch # webpack -w

4.2.2 liveload(自动刷新) + vue

1
2
3
4
5
# liveload
$ npm i webpack-dev-server -g

# vue
$ npm i vue -S

目录

1
2
3
4
5
6
7
8
9
10
├── bundle.js
├── entry.js # 1
├── first.js
├── img
├── index.html # 2
├── node_modules
├── package.json
├── selectarea.mov
├── style.css
└── webpack.config.js

# 1 entry.js

1
2
3
4
5
6
7
8
9
require('./first.js');
require('!style!css!./style.css');
var Vue = require('vue');
new Vue({
el: 'body',
data: {
message: 'Hello Vue.js'
}
});

# 2 index.html

1
2
3
<h1 id="app">
{{ message }}
</h1>

1
$ webpack-dev-server # 实时预览:http://localhost:8080/webpack-dev-server/

5 加载vue单文件组织

初始目录

1
2
3
4
5
6
.
├── index.html
├── src
│ ├── components # 组件
│ └── main.js # 入口
└── webpack.config.js

初始化依赖

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
# package.json
$ npm init

# vue
$ npm i vue -S

# webpack
$ npm i webpack -D

# babel
$ npm i babel -D # es6
$ npm i babel-core -D
$ npm i babel-plugin-transform-runtime -D
$ npm i babel-preset-es2015 -D # babel
$ npm i babel-runtime -D

# webpack loader
$ npm i autoprefixer-loader -D # css 浏览器前缀
$ npm i babel-loader -D
$ npm i css-loader -D
$ npm i file-loader -D
$ npm i html-loader -D
$ npm i node-sass -D
$ npm i sass-loader -D
$ npm i style-loader -D
$ npm i url-loader -D
$ npm i vue-html-loader -D
$ npm i vue-loader -D
$ npm i vue-style-loader -D
$ npm i vue-hot-reload-api -D

完成后目录

1
2
3
4
5
6
7
8
9
├── index.html # 1
├── node_modules
├── npm-debug.log
├── package.json # 2
├── src
│ ├── components
│ │ └── app.vue # 3
│ └── main.js # 4
└── webpack.config.js # 5

#1 index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack vue</title>
<style>
#app {
margin: 20px auto;
width: 800px;
height: 600px;
}
</style>

</head>
<body>
<div id="app"></div>
<script src="dist/main.js"></script>
</body>
</html>

#2 package.json

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
{
"name": "vue_by_webpack_blog_spa",
"version": "1.0.0",
"description": "study vue",
"main": "webpack.config.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --inline"
},
"author": "mengqingshen",
"license": "ISC",
"dependencies": {
"vue": "^1.0.26"
},
"devDependencies": {
"autoprefixer-loader": "^3.2.0",
"babel": "^6.5.2",
"babel-core": "^6.10.4",
"babel-loader": "^6.2.4",
"babel-plugin-transform-runtime": "^6.9.0",
"babel-preset-es2015": "^6.9.0",
"babel-runtime": "^6.9.2",
"css-loader": "^0.23.1",
"file-loader": "^0.9.0",
"html-loader": "^0.4.3",
"node-sass": "^3.8.0",
"sass-loader": "^4.0.0",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"vue-hot-reload-api": "^2.0.5",
"vue-html-loader": "^1.2.3",
"vue-loader": "^8.5.3",
"vue-style-loader": "^1.0.0",
"webpack": "^1.13.1"
}
}

#3 app.vue

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
<script>
/* es6 */
export default {
el:"#app",
/* data:function(){},下面是es6写法 */
data () {
return {
name:"laputa",
age:"2q1"
}
}
}
</script>


<template>
<div>
<h1>姓名:{{name}}</h1>
<h2>{{age}}</h2>
</div>
</template>

<style lang="sass">
/* 一定要加 lang 不然无法编译 */
/*测试一下对sass的编译*/
$qwe:#098;
body{
background-color: $qwe;
h1{
background-color: #eee;
color: red;
transform: translate(10%, 10%);/*测试自动添加前缀*/
}
h1:hover{
height:100px;
}
h2{
background-color: #999;
}
}
</style>

#4 main.js

1
2
3
4
5
6
7
//es6语法,其实不用写完,会自动查找。可以直接写import Vue from "vue";
import Vue from "../node_modules/vue/dist/vue.min.js";

//引入我们编写的测试用vue文件。
import app from './components/app';
Vue.config.debug = true;//开启错误提示
new Vue(app);

#5 webpack.config.js

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
/**
* NodeJS中的Path对象,用于处理目录的对象,提高开发效率。
* @type {Object}
*/

var path = require('path');

/* 模块导入 */
module.exports = {
/**
* 入口文件地址,不需要写完,会自动查找
* @type {String|Array}
*/

entry: './src/main',

/**
* 输出
* @type {Object}
*/

output: {
path: path.join(__dirname, './dist'),// 文件地址,使用绝对路径形式
filename: '[name].js',// [name]这里是 webpack 提供的根据入口文件自动生成的名字
publicPath: '/dist/'// 公共文件生成的地址
},

/**
* 服务器配置相关,自动刷新!
* @type {Object}
*/

devServer: {
historyApiFallback: true,
hot: false,// 全局刷新
inline: true,
progress: true,
},

/**
* 加载器
* @type {Object}
*/

module: {
loaders: [
/* 解析.vue文件 */
{ test: /\.vue$/, loader: 'vue' },

/* 转化ES6的语法 */
{ test: /\.js$/, loader: 'babel', exclude: /node_modules/ },

/* 编译css并自动添加css前缀 */
{ test: /\.css$/, loader: 'style!css!autoprefixer'},

/* .scss 文件想要编译,scss就需要这些东西!来编译处理
install css-loader style-loader sass-loader node-sass --save-dev */

{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},

/* 图片转化,小于8K自动转化为base64的编码 */
{ test: /\.(png|jpg|gif)$/, loader: 'url-loader?limit=8192'},

/* html模板编译 */
{ test: /\.(html|tpl)$/, loader: 'html-loader' },
]
},

/**
* .vue的配置。需要单独出来配置,其实没什么必要--因为我删了也没保错,不过这里就留这把,因为官网文档里是可以有单独的配置的。
* @type {Object}
*/

vue: {
loaders: {
css: 'style!css!autoprefixer',
}
},

/**
* 转化成es5的语法
* @type {Object}
*/

babel: {
presets: ['es2015'],
plugins: ['transform-runtime']
},

resolve: {
/* require时省略的扩展名,如:require('module') 不需要module.js */
extensions: ['', '.js', '.vue'],

/* 别名,可以直接使用别名来代表设定的路径以及其他 */
alias: {
filter: path.join(__dirname, './src/filters'),
components: path.join(__dirname, './src/components')
}
},

/* 开启source-map,webpack有多种source-map,在官网文档可以查到 */
devtool: 'eval-source-map'
};

1
2
# http://localhost:8080/
$ npm start

6 vue组件

7 vue-router

1
$ npm i vue-loader -D

目录

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── dist
│ └── main.js
├── index.html# 1 页面入口
├── node_modules
├── package.json
├── src
│ ├── components
│ │ ├── app.vue # 2 组件1,嵌套了组件2
│ │ ├── hello.vue # 3 组件2
│ │ └── list.vue # 4 组件3
│ └── main.js # 5 实例化 vue 组件;初始化路由
└── webpack.config.js

# 1 index.html

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack vue</title>
<style>
*, *:beofre, *:after {
box-sizing: border-box;
}

body, html {
height: 100%;
overflow: hidden;
}

#app {
margin: 20px auto;
width: 800px;
height: 600px;
}
</style>

</head>
<body>
<div id="app">
<!-- 路由视图 -->
<router-view></router-view>
</div>
<script src="dist/main.js"></script>
</body>
</html>

#2 app.vue

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
<script>
import hello from './hello.vue'
export default {
data () {
return {
name: 'laputa',
age: '26'
}
},
methods: {
golist () {
this.$route.router.go({name: 'list'});
}
},
components: {
hello
}
}
</script>


<template>
<div>
<span>姓名:{{name}}</span>
<span>年龄:{{age}}</span>
<div style="overflow: hidden;">
<button @click="golist">$toute.router.go查看</button>
<a v-link="{name: 'list'}">v-link查看列表</a>
</div>

<a v-link="{name: 'index'}">回去主页</a>
<a v-link="{name: 'hello'}">嵌套的路由</a>

<!-- 嵌套其它 vue 组件 -->
<hello></hello>

<!-- 嵌套路由显示视图 -->
<router-view></router-view>
</div>
</template>

<style lang="sass">
$qwe:pink;
body {
background-color: $qwe;
color: red;
}
h1 {
background-color: blue;
color: red;
}
h2 {
background-color: purple;
}
a {
display: block;
font-size: 16px;
}
button {
float: left;
}
</style>

# 3 组件2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
export default{
data () {
return {
hello:"这里是嵌套路由!!!1"
}
}
}
</script>


<template>
<div>
<h1>{{hello}}</h1>
</div>
</template>

<style scoped>
div{
text-shadow: 1px 1px 10px #000;
}
</style>

#4 组件3

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
<script>
export default {
data () {
return {
peoples: [{
name: '小红',
age: 20
}, {
name: '张三',
age: 12
}, {
name: '三五',
age: 78
}, {
name: '阿文',
age: 22
},
{
name: '李六',
age: 87
}]
}
}
}
</script>


<template>
<ul>
<li v-for="person in peoples">
{{person.name}}: {{person.age}}
</li>
</ul>
</template>

<style>
li {
height: 30px;
line-height: 30px;
list-style: none;
}
</style>

#5 main.js

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
//es6语法,其实不用写完,会自动查找。可以直接写import Vue from "vue";

/* 引入第三方库 */
import Vue from '../node_modules/vue/dist/vue.min.js';

import VueRouter from 'vue-router';

Vue.use(VueRouter);

/* 引入自定义的 vue 组件 */
import index from './components/app.vue';
import list from './components/list';
import hello from './components/hello';

Vue.config.debug = true;//开启错误提示

/* 创建根组件 */
var App = Vue.extend({});// 路由器需要一个根组件

/* 创建一个路由器实例 */
var router = new VueRouter();// 创建实例时可以传入配置参数进行定制,为保持简单,这里使用默认配置

/* 定义路由规则 */
// 每条路由规则应该映射到一个组件。这里的“组件”可以是一个使用 Vue.extend
// 创建的组件构造函数,也可以是一个组件选项对象。
router.map({
'index': {
name: 'index',
component: index,
// 在/index下设置一个子路由
// 当匹配到/index/hello时,会在index的<router-view>内渲染
subRoutes: {
'hello': {
name: 'hello',
component: hello
}
}
},
'/list': {
name: 'list',
component: list
}
})

router.redirect({
'*': 'index'
})

/* 启动应用 */
router.start(App, '#app');// 路由器会创建一个 App 实例,并且挂载到选择符 #app 匹配的元素上。

1
$ npm start