webpack从零开始搭建Vue项目
webpack从零开始搭建Vue项目
包含:vue-router/vuex/scss支持/热加载等,从创建文件夹开始,内容稍微有点长,通过这个配置可以对webpack有一个基础的了解
一、 项目初始化
在存放项目的地方打开命令行窗口,依次执行以下命令:
mkdir vue-demo
创建文件夹cd vue-demo
进入文件夹npm init -y
初始化项目
二、 webpack初始化
- 安装webpack,执行命令
npm install webpack webpack-cli -D
- 修改package.json,添加启动命令
// vue-demo/package.json
{
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack --mode development",
"build": "webpack --mode production"
},
...
}
- vue-demo下创建src文件夹,用于存放vue项目主要文件(vue-cli脚手架也是这么干的,咱也这么干,尽量接近vue-cli脚手架)
- vue-demo/src下创建main.js,作为项目的入口文件
- vue-demo下创建webpack.config.js文件,用于配置webpack
// vue-demo/webpack.config.js
const path = require('path');
module.exports = {
entry: './src/main.js', // 入口文件
output: { // 输出
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
};
- 此时已经可以执行打包了,打包命令
npm run build
,dist为打包输出的文件
三、 创建HTML模板
html模板可以让我们加入一些自定义或第三方的工具
- vue-demo下创建index.html
// vue-demo/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue&&webpack</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<style>
body{
background-color: red;
color: #fff;
}
</style>
</head>
<body>
<div id="app">
<h1>Hello World</h1>
</div>
</body>
</html>
- 使用html-webpack-plugin创建模板,执行
npm install html-webpack-plugin -D
- 修改webpack.config.js
// vue-demo/webpack.config.js
const path = require('path');
module.exports = {
entry: './src/main.js', // 入口文件
output: { // 输出
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
minify: {
collapseWhitespace: true, //折叠空白区域
removeComments: true, //删除注释
hash: true, //是否需要对src引的文件后面加上Hash,使用时需要区分开发环境和生产环境
chunks: [], //允许添加一些额外的文件
chunksSortMode: 'manual' //chunks的文件顺序注入
}
}),
]
};
- 执行打包命令
npm run build
,dist文件夹下生成了index.html,运行即可看到Hello World
四、 编译css
开发时,css都是单独的文件,我们姑且这样做
- vue-demo/src下创建common.css,将index.html中的style移植过来,为了区分,改一下颜色
// vue-demo/src/common.css
body{
background-color: #fff;
color: red;
}
// vue-demo/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue&&webpack</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>
<body>
<div id="app">
<h1>Hello World</h1>
</div>
</body>
</html>
- 修改main.js,引人css
// vue-demo/src/main.js
import './common.css'
此时执行打包命令,会报错,这是因为webpack只能处理js和json代码,处理其他代码则需要借助loader
4. 安装处理css需要的loader,执行npm install css-loader style-loader -D
5. 配置loader,修改webpack.config.js
// vue-demo/webpack.config.js 新增
module: {
rules: [{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}]
}
注意加载顺序,此处style-loader必须在css-loader之前,否则会报错,你可以试一下’
6. 执行打包命令,运行打包后的html文件,可以看到白色背景红色字体,即为成功
五、 热更新
提醒:修改各种配置文件或者安装插件时,先把项目停止,以免发生不可预期的错误
- 在集成vue之前,先实现热更新的功能,免得每次都要打包查看结果,安装插件
npm install webpack-dev-server -D
- 修改webpack.config.js
// vue-demo/webpack.config.js 新增
devServer: {
contentBase: path.join(__dirname, 'dist'), // 服务器资源的根目录,不写的话,默认为bundle.js
hot: true, //启用热加载
host: 'localhost',
port: 5000, //端口号
compress: true, // 服务器资源采用gzip压缩
open: true, // 服务器启动后打开默认浏览器
historyApiFallback: true, // 解决history模式刷新404
}
提醒:historyApiFallback是解决本地history刷新页面404的配置,线上是使用nginx配置解决
3. 修改package.json文件
// vue-demo/package.json 修改
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack serve --mode development",
"build": "webpack --mode production"
}
提醒:以前的写法是"dev": "webpack-dev-serve --mode development",现在已变更为"dev": "webpack serve --mode development",这个耽误了我不少查找时间,这里做个记录
4. 执行npm run dev
,不出意外,项目会自动打开了。(是不是跟vue脚手架有一点像了)
六、 集成Vue
上面的操作跟vue没有任何关系,现在请主角登场
- 安装vue,执行
npm install vue
- 修改main.js
// vue-demo/src/main.js 看一下控制台是否打印成功
import Vue from 'vue'
import './common.css'
console.log(Vue);
- 创建App.vue入口文件(vue-cli创建的项目是不是也有一个这个东西?这里还原他)
// vue-demo/src/App.vue 将main.js里引入css的代码去掉,在这里引入,保持main.js的整洁
<template>
<div>
<h1>{{ text }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
text: '你好,世界'
}
},
methods: {
},
}
</script>
<style>
@import url("./common.css");
</style>
- 创建vue实例
// vue-demo/src/main.js 看一下控制台是否打印成功
import Vue from 'vue'
import './common.css'
console.log(Vue);
new Vue({
render: (h) => {
return h(App)
}
}).$mount("#app")
- 重新启动项目,出乎意料的是报错了,“你好,世界”也没有显示出来
报错如下:Uncaught Error: Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file
大致意思是我们确实处理vue的loader,当然了,前面说过,webpack默认只认识js和json,所有这里我们要给他赋能 - 安装vue-loader,修改配置,执行
npm install vue-loader vue-template-compiler -D
,为什么是两个,具体可以上官网查看
// vue-demo/webpack.config.js 修改
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
entry: './src/main.js', // 入口文件
output: { // 输出
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
minify: {
collapseWhitespace: true, //折叠空白区域
removeComments: true, //删除注释
hash: true, //是否需要对src引的文件后面加上Hash,使用时需要区分开发环境和生产环境
chunks: [], //允许添加一些额外的文件
chunksSortMode: 'manual' //chunks的文件顺序注入
}
}),
new VueLoaderPlugin()
],
module: {
rules: [{
test: /\.css/,
use: [
'style-loader',
'css-loader'
]
},{
test: /\.vue$/,
use: [
'vue-loader'
]
}]
},
devServer: {
contentBase: path.join(__dirname, 'dist'), // 服务器资源的根目录,不写的话,默认为bundle.js
hot: true, //启用热加载
host: 'localhost',
port: 5000, //端口号
compress: true, // 服务器资源采用gzip压缩
open: true, // 服务器启动后打开默认浏览器
historyApiFallback: true, // 解决history模式刷新404
}
};
- 运行项目
npm run dev
,红色的“你好,世界”出来了吧,看到有一个英文闪一下,删掉idnex.html #ppp 里的内容
七、 集成scss
这个跟vue-cli项目一样的集成方式,这里再来一遍
- 安装,执行
npm install node-sass sass-loader -D
,修改webpack.config.js
// vue-demo/webpack.config.js module新增
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}
至此,此项目的webpack配置文件我们配置完了,如需其他配置可上官网查看
2. 将vue-demo/src/common.css改为common.scss,App.vue引用也改成@import url("./common.scss")
// vue-demo/src/App.vue
<template>
<div>
<h1>{{ text }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
text: '你好,世界'
}
},
methods: {
click() {
this.lang = this.lang === 'ch' ? 'en' : 'ch'
}
},
}
</script>
<style lang="scss">
@import url("./common.scss");
</style>
- 启动项目,不报错且能看到红色字体,则说明scss集成成功
八、 集成vue-router
- 安装vue-router,
npm install vue-router
- vue-demo/src下创建pages目录,并在pages下创建4个vue页面,分别命名为pageA、pageB、pageC、pageD
// vue-demo/src/pages/pageA 其他页面跟这个一样,只是h1里的内容不一样
<template>
<div>
<h1>pageA</h1>
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
</style>
- vue-demo/src下创建route文件夹,并在route下创建index.js,作为路由配置文件
// vue-demo/src/route/index.js 其中配置了路由及子路由
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
export default new VueRouter({
mode: 'history',
routes: [
{
path: '/',
redirect: { name: 'pageA' },
},
{
name: 'pageA',
path: '/pageA',
component: () => import('../pages/pageA.vue'),
children: [
{
name: 'pageB',
path: 'pageB',
component: () => import('../pages/pageB.vue')
},
{
name: 'pageC',
path: 'pageC',
component: () => import('../pages/pageC.vue')
}
]
},
{
name: 'pageD',
path: '/pageD',
component: () => import('../pages/pageD.vue')
}
]
})
- main.js注入vue,并启动项目,此时浏览器为http://localhost:5000/pageA,恭喜,路由集成成功
// vue-demo/src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
router,
render: (h) => {
return h(App)
}
}).$mount("#app")
- 接下来验证一下子路由,修改pageA
// vue-demo/src/pages/pageA
<template>
<div>
<h1>我是index页面</h1>
<a @click="changeRoute('pageB')">跳转pageB子路由</a>
<a @click="changeRoute('pageC')">跳转pageC子路由</a>
<router-view></router-view>
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
changeRoute(name) {
this.$router.push({name})
}
}
}
</script>
<style lang="scss" scoped>
a{
color: #169DFA;
margin-right: 20px;
}
</style>
点击子路由按钮,分别跳转到不同路由,页面也有变化,恭喜,子路由也没有问题
九、 集成vuex
为了验证vuex,在vue-demo/src下新建一个components目录,熟悉vue的知道接下来要干嘛了,没错,创建公共组件
- vue-demo/src/components下创建Footer.vue
// vue-demo/src/components/Footer.vue
<template>
<div class="footer">
<div>
<div @click="changeRoute('pageA')">pageA</div>
<div @click="changeRoute('pageD')">pageD</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
changeRoute(name) {
this.$router.replace({name})
}
}
}
</script>
<style lang="scss" scoped>
.footer{
width: 100vw;
height: 50px;
&>div{
position: fixed;
z-index: 1;
left: 0;
bottom: 0;
width: 100vw;
height: 50px;
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px solid #ddd;
&>div{
display: flex;
align-items: center;
justify-content: center;
flex: 1;
height: 100%;
}
}
}
</style>
- 在App.vue引用组件
<template>
<div>
<h1>{{ text }}</h1>
<router-view></router-view>
<Footer/>
</div>
</template>
<script>
import Footer from './components/Footer.vue'
export default {
components: {
Footer
},
data() {
return {
text: '你好,世界'
}
},
methods: {
},
}
</script>
<style lang="scss">
@import url("./common.scss");
</style>
此时已经可以切换pageA、pageB、pageC、pageD四个路由了,接下来集成vuex
- 安装vuex,
npm isntall vuex
- vue-demo/src下新建store文件夹,并在store下新建index.js作为vuex配置
vue-demo/src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
这里store我们使用官方案例,做一个计数器功能
5. main.js注入vuex
vue-demo/src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './route'
import store from './store'
new Vue({
router,
store,
render: (h) => {
return h(App)
}
}).$mount("#app")
- 验证vuex,在pageA、pageD中分别加入验证的代码
//pageA
<template>
<div>
<h1>我是index页面</h1>
<a @click="changeRoute('pageB')">跳转pageB子路由</a>
<a @click="changeRoute('pageC')">跳转pageC子路由</a>
<router-view></router-view>
<button @click="add">加1</button>
<h2>vuex共享数据计数器的值:{{count}}</h2>
</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count
}
},
methods: {
changeRoute(name) {
this.$router.push({name})
},
add(){
this.$store.commit('increment')
}
}
}
</script>
<style lang="scss" scoped>
a{
color: #169DFA;
margin-right: 20px;
}
</style>
// pageD
<template>
<div>
<h1>pageD</h1>
<button @click="add">加1</button>
<h2>vuex共享数据计数器的值:{{count}}</h2>
</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count
}
},
methods: {
add(){
this.$store.commit('increment')
}
}
}
</script>
此时在apgeA和pageD中点击按钮,无论切换到另外哪个路由,可以看到计数器的值已经实现共享了,至此vuex集成完毕
一个简单vue项目诞生了,完毕!!!