从零搭建webpack4 + vue + vue-rout + vuex项目架构

配置前说明:确保已安装node环境。

检查是否安装node环境:

  1、终端输入node -v或node --version
  2、终端输入npm -v或npm --version

一、配置前命令简写说明:

1、npm inpm install区别:
a)npm i是npm install缩写。
b)用npm i安装的模块无法用npm uninstall删除,用npm uninstall i才卸载掉。
c)npm i会帮助检测与当前node版本最匹配的npm包版本号,并匹配出来相互依赖的npm包应该提升的版本号。
d)部分npm包在当前node版本下无法使用,必须使用建议版本。
e)安装报错时intall肯定会出现npm-debug.log 文件,npm i不一定。
2、npm -Dnpm -S的区别:
a)-S就是--save的简写。
b)-D就是--save-dev 这样安装的包的名称及版本号就会存在package.json的devDependencies这个里面,而--save会将包的名称及版本号放在dependencies里面。

二、webpack基础配置(开发环境):

命令运行前提:终端进入项目目录文件夹里,以下不再说明。
1、创建一个项目目录demo,使用NPM初始化配置:
npm init
执行后,会有一系列选项,可以按回车快速确认,完成后,会在demo目录生成一个package.json文件。
2、使用NPM安装webpack模块、webpack-dev-server模块和webpack-cli模块:
npm install webpack --save-dev
npm install webpack-dev-server --save-dev
npm i -D webpack-cli
安装成功后,在package.json中会多几项配置:

"devDependencies": {
    "webpack": "^4.29.6",
    "webpack-cli": "^3.2.3",
    "webpack-dev-server": "^3.2.1"
  }

3、在demo目录下创建webpack.config.js并初始化内容:

const config = {

};
module.exports = config;

4、在package.jsonscripts里增加一个快速启动webpack-dev-server服务的脚本和打包脚本:

{
  //...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --open --config=webpack.config.js",
    "build": "webpack --progress --hide-modules"
  },
  //...
}

其中--config是指向webpack-dev-server读取的配置文件路径。
--open会执行命令时自动在浏览器打开页面,默认地址是127.0.0.1:8080
IP和端口都是可以配置的,例如:

//...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --host 172.172.172.1 --port 8888 --open --config=webpack.config.js"
  },
  //...

这样访问地址就改为了172.172.172.1:8888
5、入口(Entry)出口(Output)

webpack配置中最重要也是必选的两项是入口(Entry)和出口(Output)。
入口的作用是告诉webpack从哪里开始寻找依赖,并且编译。
出口的作用是用来配置编译后的文件存储位置和文件名。

在demo目录下新建一个空的main.js作为入口的文件,然后在webpack.config.js中进行入口和出口的配置:

const path = require('path');

const config = {
  entry: {
    main: './main'
  },
  output: {
    path: path.join(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'main.js'
  }
};

entry中的main就是配置的单入口,webpack会从main.js文件开始工作。
output中path选项用来存放打包后文件的输出目录,是必填项。
publicPath指定资源文件引用的目录,如果你的资源存放在CDN上,这里可以填CDN的网址。
filename用于指定输出文件的名称。
因此,这里配置的output意为打包后的文件会存储为demo/dist/main.js,只要在html中引入即可。

在demo目录下新建一个index.html作为项目的入口:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1 user-scalable=0">
    <title>Demo</title>
</head>
<body>
    <div id="app"></div>
    <script src="./dist/main.js"></script>
</body>
</html>

运行npm run build命令,后再执行npm run dev即可启动了一个空项目。
6、webpack加载器(Loaders):
webpack通过安装不同的加载器(Loaders)可以对各种后缀名的文件进行处理。
css-loaderstyle-loader,通过NPM安装:
npm install css-loader --save-dev
npm install style-loader --save-dev
安装完成后,在webpack.config.js文件里配置Loader,增加对.css文件的处理:

const path = require('path');

const config = {
  //...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'css-loader',
          'style-loader'
        ]
      }
    ]
  }
};
module.exports = config;

在module对象的rules属性中可以指定一系列的loaders,每一个loader都必须包含testuse两个选项。这段配置的意思是说,当webpack编译过程中遇到require()import语句导入一个后缀名为.css的文件时,先将它通过css-loader转换,然后继续打包。
use选项的值可以是数组或字符串,如果是数组,它的编译顺序就是从后往前。

在demo目录下新建一个style.css的文件,作为全局css样式,并在main.js中导入:

//main.js
import './style.css';

7、webpack插件(Plugins):
通过NPM安装extract-text-webpack-plugin插件:
npm install extract-text-webpack-plugin@next --save-dev
然后在webpack.config.js配置文件中导入插件,并改写loader的配置和index.html:

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

const config = {
    //...
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    { loader: "style-loader" },
                    { loader: "css-loader" }
                ]
            }
        ]
    },
    plugins: [
        // 重命名提取后的css文件
        new ExtractTextPlugin('main.css')
    ]
};

module.exports = config;
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1 user-scalable=0">
    <title>Demo</title>
    <link rel="stylesheet" href="./dist/main.css">
</head>
...

到此,基本的webpack配置就写完了。webpack虽然看似复杂,但它只不过是一个js配置文件,只要搞清楚入口(Entry)出口(Output)加载器(Loaders)插件(Plugins)这四个概念,使用起来就不那么困惑了。

三、单文件组件和vue-loader:

一个.vue文件一般包含3部分,即<template><script><style>,如下所示:

//component.vue
<template>
  <div>
    <span>你好:{{ name }}</span>
  </div>
</template>
<script>
  export default {
    props: {
      name: {
        type: String,
        default: ''
      }
    }
  }
</script>
<style scoped>
  span {
    color: #f60;
  }
</style>

在component.vue文件中,<template></template>之间的代码就是该组件的模板HTML,<style></style>之间的是css样式,示例中的<style>标签使用了scoped属性,表示当前的css只在这个组件有效,如果不加,那么span的样式会应用到整个项目。

使用.vue文件需要先安装vue-loader、vue-style-loader等加载器并做配置。因为要使用ES6语法,还需要安装babel和babel-loader等加载器。使用NPM逐个安装以下依赖:
npm install --save vue
npm install --save-dev vue-loader
npm install --save-dev vue-style-loader
npm install --save-dev vue-template-compiler
npm install --save-dev vue-hot-reload-api
npm install --save-dev babel
npm i babel-loader@7.1.5 -D
npm install --save-dev babel-core
npm install --save-dev babel-plugin-transform-runtime
npm install --save-dev babel-preset-es2015
npm install --save-dev babel-runtime
安装完成后,修改配置文件webpack.config.js来支持对.vue文件及ES6的解析:

// webpack.config.js
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

const config = {
    entry: {
        main: './main'
    },
    output: {
        path: path.join(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'main.js'
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader',
                options: {
                    loaders: {
                        css: ExtractTextPlugin.extract({
                            use: 'css-loader',
                            fallback: 'vue-style-loader'
                        })
                    }
                }
            },
            {
                test: /\.js$/,
                use: [
                    {
                        loader: 'babel-loader'
                    }
                ],
                exclude: /node_module/
            },
            {
                test: /\.css$/,
                use: [
                    { loader: "style-loader" },
                    { loader: "css-loader" }
                ]
            }
        ]
    },
    plugins: [
        // 重命名提取后的css文件
        new ExtractTextPlugin('main.css'),
        new VueLoaderPlugin()
    ]
};

module.exports = config;

vue-loader在编译.vue文件时,会对<template><script><style>分别处理,所以在vue-loader选项里多了一项options来进一步对不同语言进行配置。比如在对css进行处理时,会先通过css-loader解析,然后把处理结果再交给vue-style-loader处理。

在demo目录下新建一个名为.babelrc的文件,并写入babel的配置,webpack会依赖此配置文件来使用babel编译ES6代码:

{
    "presets": ["es2015"],
    "plugins": ["transform-runtime"],
    "comments": false
}

配置好后,就可以使用.vue文件了。每个.vue文件就代表一个组件,组件之间可以相互依赖。

四、webpack用于生产环境:

1、先对webpack进一步配置,来支持更多常用的功能:
通过NPM安装url-loaderfile-loader来支持图片、字体等文件:
npm install --save-dev url-loader
npm install --save-dev file-loader

// webpack.config.js
const config = {
  //...
  module: {
    rules: [
      //...
      {
        test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
        loader: 'url-loader?limit=1024'
      }
    ]
  }
};

当遇到.gif.png.ttf等格式文件时,url-loader会把它们一起编译到dist目录下,"?limit=1024"是指如果这个文件小于1kb就以base64的形式加载,不会生成一个文件。
webpack.config.jsoutput选项里已经指定了pathpubilcPath,打完包后,所有的资源都会保存在demo/dist目录下。
2、为了方便开发和生产环境的切换,在demo目录下再新建一个用于生产环境的配置文件webpack.prod.config.js
打包会用到下面两个依赖,使用NPM安装:
npm install --save-dev webpack-merge
npm install --save-dev html-webpack-plugin
package.json中,再加入一个probuild的快捷脚本用来打包:

{
  //...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --open --config=webpack.config.js",
    "build": "webpack --progress --hide-modules",
    "probuild": "webpack --mode production --progress --hide-modules --config=webpack.prod.config.js"
  },
  //...
}

webpack.prod.config.js代码如下:

// webpack.prod.config.js
const webpack = require('webpack');
const HtmlwebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const merge = require('webpack-merge');
const webpackBaseConfig = require('./webpack.config.js');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

// 清空基本配置的插件列表
webpackBaseConfig.plugins = [];

module.exports = merge(webpackBaseConfig, {
    output: {
        publicPath: '/dist/',
        // 将入口文件重命名为带有20位hash值得唯一文件
        filename: '[name].[hash].js'
    },
    // 定义当前为生产环境
    mode: 'production',
    plugins: [
        new ExtractTextPlugin({
            filename: '[name].[hash].css',
            allChunks: true
        }),
        // 提取模板,并保留入口html文件
        new HtmlwebpackPlugin({
            filename: './index_prod.html',
            template: './index.html',
            minify: {
                //对 html 文件进行压缩,minify 的属性值是一个压缩选项或者 false 。默认值为false, 不对生成的 html 文件进行压缩
                removeComments:true, // 去除注释
                collapseWhitespace: true //是否去除空格
            }
        }),
        new VueLoaderPlugin()
    ],
    // 对js文件进行压缩
    optimization: {
        splitChunks: {
            chunks: 'all'
        },
        runtimeChunk: true
    }
})

上面安装的webpack-merge模块就是用于合并两个webpack的配置文件,所以prod的配置是在webpack.config.js基础上扩展的。
html-webpack-plugin是用来生成html文件的,它通过template选项来读取指定的模板index.html,然后输入到filename指定的目录,就是demo/index_prod.html

五、vue-router配置:

1、在demo目录下新建一个app.vue文件并写入以下内容:

<!-- app.vue -->
<template>
    <div>
        <router-view></router-view>
    </div>
</template>
<script>
    export default {
        name: 'app'
    }
</script>

app.vue里的路由视图<router-view>挂载所有的路由组件。
运行网页时,<router-view>会根据当前路由动态渲染不同的页面组件。路由切换时,切换的是<router-view>挂载的组件,其他的内容并不会变化。
2、通过NPM安装vue-router:
npm install --save vue-router
main.js里使用Vue.use()加载插件:

import './style.css';
//导入Vue框架
import Vue from 'vue';
//导入vue-router路由
import VueRouter from 'vue-router';
//导入app.vue组件
import App from './app.vue';

Vue.use(VueRouter);

//创建Vue根实例
new Vue({
  el: '#app',
  render: h => h(App)
});

每个页面对应一个组件,也就是对应一个.vue文件。
3、在demo目录下创建views文件夹,用于存放所有的页面,然后在views创建index.vue文件并写入以下内容:

<!-- index.vue -->
<template>
    <div>
        <h1>首页</h1>
    </div>
</template>
<script>
    export default {
        name: 'index'
    }
</script>

4、再回到main.js,完成路由的剩余配置。创建一个数组来制定路由匹配列表,每一个路由映射一个组件:

//main.js
const Routers = [
    {
        path: '/index',
        component: (resolve) => require(['./views/index.vue'], resolve)
    },
    // 当访问路径不存在时,重定向到首页
    {
        path: '*',
        redirect: '/index'
    }
]

Routers里每一项的path属性就是指定当前匹配的路径,component是映射的组件。上例的写法,webpack会把每一个路由都打包为一个js文件,在请求到该页面时,才去加载这个页面的js,也就是异步实现的懒加载(按需加载)。这样做的好处是不需要在打开首页的时候就把所有的页面内容全部加载进来,只在访问时才加载。
5、使用了异步路由后,编译出的每个页面的js都叫做chunk(块),它们命名默认是0.main.js、1.main.js......可以在webpack配置的出口output里通过设置chunkFilename字段修改chunk命名,例如:

//webpack.config.js
output: {
        path: path.join(__dirname, './dist'),
        publicPath: '/dist/',
        filename: '[name].js',
        chunkFilename: '[name].chunk.js'
    },

有了chunk后,在每个页面(.vue文件)里写的样式也需要配置后才会打包进main.css,配置插件:

//webpack.config.js
plugins: [
        // 重命名提取后的css文件
        new ExtractTextPlugin({
            filename: '[name].css',
            allChunks: true
        }),
        new VueLoaderPlugin()
    ]

6、继续在main.js里完成配置和路由实例:

//main.js
const RouterConfig = {
    // 使用HTML5的History路由模式
    mode: 'history',
    routes: Routers
};
const router = new VueRouter(RouterConfig);

//创建Vue根实例
new Vue({
    el: '#app',
    router: router,
    render: h => h(App)
});

RouterConfig里,设置modehistory会开启HTML5History路由模式,通过“/”设置路径。如果不配置mode,就会使用“#”来设置路径。开启History路由,在生产环境时服务端必须进行配置,将所有路由都指向同一个html或设置404页面为该html,否则刷新时页面会出现404。
7、webpack-dev-server也要配置下来支持History路由,在package.json中修改dev命令:

"scripts": {
    //...
    "dev": "webpack-dev-server --open --history-api-fallback --config=webpack.config.js",
    //...
  },

增加了--history-api-fallback,所有的路由都会指向index.html。

六、vuex配置:

1、通过NPM安装Vuex:
npm install --save vuex
它的用法与vue-router类似,在main.js里,通过Vue.use()使用Vuex:

// main.js
import './style.css';
//导入Vue框架
import Vue from 'vue';
//导入vue-router路由
import VueRouter from 'vue-router';
//导入Vuex
import Vuex from 'vuex';
//导入app.vue组件
import App from './app.vue';

Vue.use(VueRouter);
Vue.use(Vuex);

//路由配置
//省略...

const store = new Vuex.Store({
    // vuex的配置
});

//创建Vue根实例
new Vue({
    el: '#app',
    router: router,
    // 使用vuex
    store: store,
    render: h => h(App)
});

仓库store包含了应用的数据(状态)和操作过程。Vuex里的数据都是响应式的,任何组件使用同一store的数据时,只要store的数据变化,对应的组件也会立即更新。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容