1.需求目标
大家开发业务时最顺手的是vue-cli,大部分的封装配置都做好了,这篇文章是用webpack4搭建这样的环境,
那vue-cli已经配置好了功能呢?
1. ES6代码转换成ES5代码
2. scss/sass/less/stylus转css
3. .vue文件转换成js文件
4. 使用 jpg、png,font等资源文件
5. 自动添加css各浏览器产商的前缀
6. 代码热更新
7. 资源预加载
8. 每次构建代码清除之前生成的代码
9. 定义环境变量
10. 区分开发环境打包跟生产环境打包
2.搭建webpack环境
2.1 初始化项目
执行npm init命令,生成基本的配置信息,和一个package.json文件
npm init
2.2 安装webpack
//安装特定的版本后面可以接@version
npm install --save-dev webpack@<version>
npm install --save-dev webpack-cli
3 开始配置功能
- 在根目录下创建build文件夹,用来存放配置文件
- 在build文件夹中创建webpack.config.js,这个文件就是用来配置功能的文件
在package.json中配置脚本命令,这样我们就可以用 npm run dev命令 启动项目了.
3.1 配置ES 6/7/8 转ES5代码功能
安装相关依赖文件:
npm install babel-loader babel-core babel-preset-env webpack
3.2配置scss转css
安装相关依赖文件,官网安装如下:
npm install sass-loader node-sass webpack --save-dev
但我这里配置的是dart-sass,把node-sass换成dart-sass即可
因为处理的顺序是sass-loader->css-loader->style-loader,顺序不能乱,里面的参数可以根据需要自行添加
sass-loader, dart-sass主要是将 scss/sass 语法转为css
css-loader主要是解析 css 文件
style-loader 主要是将 css 解析到 html页面 的 style 上
3.3 配置postcss实现CSS3添加浏览器前缀
添加依赖文件
npm install --save-dev autoprefixer
前面的配置不变,只要在,sass-loader后面再添加一个postcss-loader
3.4 配置html-webpack-plugin 创建html启动首页
1.安装依赖文件
npm install --save-dev html-loader
2.新建 ./public/index.html, 注意这里一定要添加一个id为app的div!!!!!!
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
在最外面先引用,再放在plugins里
const HtmlWebpackPlugin = require("html-webpack-plugin");
3.5 配置devServer 热更新功能
1.安装devServer依赖文件
npm install webpack-dev-server --save-dev
2.修改webpack.config.js
//module.exports外面
let webpack = require('webpack');
//module.exports里面
devServer: {
contentBase: "./dist", //要放在服务器上的文件名
port: 3003, //设置端口
open: true, //设置浏览器自动打开
hotOnly: true, //只热更新,不刷新浏览器
},
plugins:[
//热更新模块插件
new webpack.HotModuleReplacementPlugin()
],
3.6 配置file-loader 打包 图片、媒体、字体等文件
安装file-loader依赖文件
npm install --save-dev file-loader
{
//url-loader 功能与 file-loader 类似,如果文件小于限制的大小。
//则会返回 base64 编码,否则使用 file-loader 将文件复制到输出的目录中
test: /\.(jpe?g|png|gif)$/i,
use: [
{
loader: "url-loader",
options: {
limit: 4096,
fallback: {
loader: "file-loader",
options: {
esModule: false,
name: "[name].[hash:8].[ext]",
},
},
},
},
],
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use: [
{
loader: "url-loader",
options: {
limit: 4096,
fallback: {
loader: "file-loader",
options: {
name: "media/[name].[hash:8].[ext]",
},
},
},
},
],
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
use: [
{
loader: "url-loader",
options: {
limit: 4096,
fallback: {
loader: "file-loader",
options: {
name: "fonts/[name].[hash:8].[ext]",
},
},
},
},
],
},
4 配置vue相关功能,识别.vue结尾的文件
npm install vue-loader vue-template-compiler cache-loader thread-loader -D
npm install vue -S
vue-loader 用于解析.vue文件
vue-template-compiler 用于编译模板
cache-loader 用于缓存loader编译的结果
thread-loader 使用 worker 池来运行loader,每个 worker 都是一个 node.js 进程。
const VueLoaderPlugin = require("vue-loader/lib/plugin");
module:{
rules:[
{
test: /\.vue$/,
exclude: /node_modules/,
use: [
{
loader: "cache-loader",
},
{
loader: "thread-loader",
},
{
loader: "vue-loader",
options: {
compilerOptions: {
preserveWhitespace: false,
},
},
},
],
},
]
plugins:[
new VueLoaderPlugin()
]
1.测试一下,在 src 新建一个 App.vue
// src/App.vue
<template>
<div class="App">
Hello World
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.App {
color: skyblue;
}
</style>
2.修改main.js
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: h => h(App)
}).$mount('#app') //这个#app就是挂载到上面说的/public/index.html上的.
5自定义环境变量
webpack自带的DefinePlugin 允许创建一个在编译时可以配置的全局常量。
plugins:[
new webpack.DefinePlugin({
'SERVICE_URL': JSON.stringify('http://localhost:3003') //这样就不用写一长串字符了
})
]
6 区分生产环境和开发环境
把webpack.config.js分为3份,
- webpack.dev.config.js 开发环境使用
- webpack.prod.config.js 生产环境使用
- webpack.config.js 公用配置
6.1 开发环境
1. 不需要压缩代码
2. 需要热更新
3. css不需要提取到css文件
6.2 生产环境
1. 压缩代码
2. 不需要热更新
3. 提取css,
4. 压缩css文件
还需要的额外依赖文件:自己去npm下载
css-minimizer-webpack-plugin 用于压缩css代码
mini-css-extract-plugin 用于提取css到文件中
clean-webpack-plugin 用于删除上次构建的文件
webpack-merge 合并 webpack配置
copy-webpack-plugin 用户拷贝静态资源
6.2 webpack.config.js
const path = require("path");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const CopyPlugin = require("copy-webpack-plugin");
//公共配置
module.exports = {
entry: {
main: [
path.resolve(__dirname, "../src/main.js"), //入口文件为/src/main.js
],
},
output: {
filename: "js/[name].[hash].js", //[name]][hash].js,name可以设置,
chunkFilename: "js/[name].[hash].js", //异步加载的模块放在这里
path: path.resolve(__dirname, "../dist"), //设置输出文件夹跟build同一级
publicPath: "/",
},
devServer: {
contentBase: "./dist", //要放在服务器上的文件名
port: 3003, //设置端口
open: true, //设置浏览器自动打开
hotOnly: true, //只热更新,不刷新浏览器
},
module: {
rules: [
{
//处理html
test: /\.(html)$/,
use: {
loader: "html-loader",
options: {
//默认attrs=img:src,处理img的src属性,
attributes: true,
},
}
},
{
test: /\.vue$/,
exclude: /node_modules/,
use: [
{
loader: "cache-loader",
},
{
loader: "thread-loader",
},
{
loader: "vue-loader",
options: {
compilerOptions: {
preserveWhitespace: false,
},
},
},
],
},
{
//url-loader 功能与 file-loader 类似,如果文件小于限制的大小。
//则会返回 base64 编码,否则使用 file-loader 将文件复制到输出的目录中
test: /\.(jpe?g|png|gif)$/i,
use: [
{
loader: "url-loader",
options: {
limit: 4096,
fallback: {
loader: "file-loader",
options: {
esModule: false, //html-loader可以解析img的src图片
name: "[name].[hash:8].[ext]",
},
},
},
},
],
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use: [
{
loader: "url-loader",
options: {
limit: 4096,
fallback: {
loader: "file-loader",
options: {
name: "media/[name].[hash:8].[ext]",
},
},
},
},
],
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
use: [
{
loader: "url-loader",
options: {
limit: 4096,
fallback: {
loader: "file-loader",
options: {
name: "fonts/[name].[hash:8].[ext]",
},
},
},
},
],
},
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env",
],
//路由懒加载
plugins: ["@babel/plugin-syntax-dynamic-import"],
},
},
},
],
},
resolve: {
alias: {
vue$: "vue/dist/vue.runtime.esm.js",
},
extensions: [".js", ".vue"],
},
plugins: [
new HtmlWebpackPlugin({
//要打包的文件
template: path.resolve(__dirname,'../public/index.html' ),
}),
new VueLoaderPlugin(),
new CleanWebpackPlugin("dist", {
root: path.resolve(__dirname, "../"),
}),
new BundleAnalyzerPlugin(),
//拷贝资源文件
new CopyPlugin([
{
from: path.resolve(__dirname, "../src", "assets"),
to: path.resolve(__dirname, "../dist", "assets")
}
],
),
//
],
};
6.3 webpack.dev.config.js
const webpack = require('webpack');
const merge=require('webpack-merge');
const common=require('./webpack.config')
//开发环境配置
module.exports =merge(common,{
devtool:'eval-cheap-module-source-map',
module:{
rules:[
{
//匹配以.scss结尾的文件
test: /\.scss$/,
//使用哪个加载器,这边顺序不能乱。
use: [
"style-loader",
{
loader: "css-loader",
options: {
sourceMap: true,
},
},
{
loader: "sass-loader",
options: {
// Prefer `dart-sass`
implementation: require("sass"),
},
},
{
loader: "postcss-loader",
options: {
sourceMap: true,
plugins:
[
require('autoprefixer'),
],
},
}
],
}
]
},
plugins:[
//热更新模块插件
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
APP_BASE_URL:JSON.stringify('http://localhost:3003')
})
],
})
6.3 webpack.prod.config.js
const MiniCssExtractPlugin = require(“mini-css-extract-plugin”);
const CssMinimizerPlugin = require(“css-minimizer-webpack-plugin”);
let merge=require(‘webpack-merge’);
let common=require(’./webpack.config’)
//生产环境配置
module.exports =merge(common,{
devtool:'source-map',
module:{
rules:[
{
//匹配以.scss结尾的文件
test: /\.scss$/,
//使用哪个加载器,这边顺序不能乱。
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
//Allows to enables/disables or setups number of loaders applied before CSS loader
// 0 => no loaders (default);
// 1 => postcss-loader;
// 2 => postcss-loader, sass-loader
importLoaders: 2,
},
},
{
loader: "sass-loader",
options: {
// Prefer `dart-sass`
implementation: require("sass"),
},
},
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"autoprefixer",
{
// Options
},
],
],
},
},
},
],
},]
},
plugins:[
//打包css插件
new MiniCssExtractPlugin({
filename: 'assets/css/[name].[contenthash:8].css',
chunkFilename: 'assets/css/[name].[contenthash:8].css'
}
),
],
//css优化压缩插件
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin({
minimizerOptions: {
preset: [
"default",
{
//删除注释
discardComments: { removeAll: true },
},
],
},
}),
],
},
})
7 集成VueRouter,Vuex
先安装依赖文件
npm install vue-router vuex --save
7.1 集成VueRouter
新增视图组件 在 src 目录下新增两个视图组件 src/views/Home.vue 和 src/views/About.vue
// src/views/Home.vue
<template>
<div class="Home">
<h2>Home</h2>
</div>
</template>
<script>
export default {
name: 'Home',
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
</style>
About.vue大致一样,更换下Home->About,
新增路由配置文件
// src/router/index.js
import Vue from 'vue'
import VueRouter from "vue-router";
import Home from '../views/Home';
import About from '../views/About';
Vue.use(VueRouter)
export default new VueRouter({
mode: 'hash',
routes: [
{
path: '/Home',
component: Home
},
{
path: '/About',
component: About
},
{
path: '*',
redirect: '/Home'
}
]
})
修改main.js
// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
router,
render: h => h(App)
}).$mount('#app')
修改App.vue
// App.vue
// 在 template 中添加
// src/App.vue
<template>
<div class="App">
Hello World
</div>
<div>
// router-link 组件 用来导航到哪个路由
<router-link to="/Home">go Home</router-link>
<router-link to="/About">go About</router-link>
</div>
<div>
// 用于展示匹配到的路由视图组件
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.App {
color: skyblue;
}
</style>
运行 npm run start
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fpYESlOF-1628257162283)(https://imgblog.csdnimg.cn/92b5108bc54e42c593b00c801ae5b083.gif#pic_center)]
7.2 集成Vuex
在src下新建/store/index.js
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
counter: 0
}
const actions = {
add: ({commit}) => {
return commit('add')
}
}
const mutations = {
add: (state) => {
state.counter++
}
}
const getters = {
getCounter (state) {
return state.counter
}
}
export default new Vuex.Store({
state,
actions,
mutations,
getters
})
修改 main.js 文件 导入 vuex
// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store' // ++
new Vue({
router,
store, // ++
render: h => h(App)
}).$mount('#app')
修改 App.vue ,查看 vuex 配置效果
// App.vue
<template>
<div class="App">
<div>
<router-link to="/Home">go Home</router-link>
<router-link to="/About">go About</router-link>
</div>
<div>
<p>{{getCounter}}</p>
<button @click="add">add</button>
</div>
<div>
<router-view></router-view>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'App',
data() {
return {};
},
computed: {
...mapGetters(['getCounter'])
},
methods: {
...mapActions(['add'])
}
};
</script>
<style lang="scss" scoped>
.App {
text-align: center;
color: skyblue;
font-size: 28px;
}
</style>
8 总结
1.package.json里的包老是有兼容性问题,这个问题是完成这次任务90%的障碍,不要默认安装最新的版本,去npm
找低一点的版本安装.
2.把package.json的里各种依赖包后面的^删除,定死版本,否则下次npm install,大概率报错.
3.要有耐心,出错别怕,多找相似原因,多google,*,百度...
最后放上代码地址,供大家参考
https://gitee.com/httpxiaobocom/vuecli.git
参考文章 :https://juejin.cn/post/6844903833160646663#heading-22