vue.js的vue-cli的理解:
vue-cli是Vue的脚手架工具,帮我们写好vue.js基础代码的工具,搞定目录结构、本地调试、代码部署、热加载、单元测试等工作。
vue.js开发环境搭建
1、安装node.js,下载地址,安装好之后需要配置node.js系统环境变量,这里要保证node.js的版本是4版本以上
2、安装全局vue-cli脚手架,用于帮助搭建所需的模板框架
首先:在cmd里面输入:npm install -g vue-cli,回车
然后:在cmd里面输入vue,出现vue信息说明安装成功,如下图所示
3、创建项目
在cmd里面输入:vue init webpack sell(项目文件夹名),等待一会会出现'git'的项,可按下图操作
4、安装依赖(进入相应的盘符路径,在执行下面的操作)
首先,在cmd里面输入:cd sell(项目),回车
然后,在cmd里面输入:npm install,回车。
这时候需要等待一会,安装成功会出现下面的图片信息:
回到项目目录下,会发现多了node_modules文件夹(里面的内容就是之前安装的依赖)
5、测试环境是否搭建成功
首先,在cmd里面输入: npm run dev
然后会出现下面的图,表示成功:
然后,在浏览器地址栏里面输入地址:localhost://8080
出现下面的图,表示环境搭建成功
6、vue实现ajax获取后台数据是通过vue-resource这个插件的,那么就需要在这个项目下安装这个插件
1) 在项目中引入该插件:在cmd下进入项目的根目录,然后输入“npm install vue-resource --save”
2)在main.js中引入该插件:import VueResource from 'vue-resoucre ;Vue.use(VueResource)
经过以上两步就可以使用vue-resource插件中的API了
新版本的vue在使用vue-cli搭建项目的框架的时候,在build文件夹下已经没有dev-server.js文件,这是因为在新版本的vue下,已经将dev-server.js和webpack.dev.conf.js进行合并了,如果需要写路由相关配置,需要在build文件夹下的webpack.dev.conf.js文件中找到devServer对象对相关属性进行配置。
下面是webpack.dev.conf.js文件的内容:
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const appData = require('../data.json');
const seller = appData.seller;
const goods = appData.goods;
const ratings = appData.ratings;
const devWebpackConfig = merge(baseWebpackConfig,{
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap,usePostCSS: true })
},// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,// these devServer options should be customized in /config/index.js
devServer: {
before(app){
app.get('/api/seller',function (req,res) { //通过路由获取数据都要通过api这个组件去获取
res.json({
errno:0,data:seller
});
});
app.get('/api/goods',function(req,res){
res.json({
errno:0,data:goods
});
});
app.get('/api/ratings',data:ratings
});
});
},clientLogLevel: 'warning',historyApiFallback: {
rewrites: [
{ from: /.*/,to: path.posix.join(config.dev.assetsPublicPath,'index.html') },],},hot: true,contentBase: false,// since we use CopyWebpackPlugin.
compress: true,host: HOST || config.dev.host,port: PORT || config.dev.port,open: config.dev.autoOpenBrowser,overlay: config.dev.errorOverlay
? { warnings: false,errors: true }
: false,publicPath: config.dev.assetsPublicPath,proxy: config.dev.proxyTable,quiet: true,// necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,}
},plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),new webpack.HotModuleReplacementPlugin(),new webpack.NamedModulesPlugin(),// HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',template: 'index.html',inject: true
}),// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname,'../static'),to: config.dev.assetsSubDirectory,ignore: ['.*']
}
])
]
})
module.exports = new Promise((resolve,reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err,port) => {
if (err) {
reject(err)
} else {
// publish the new Port,necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})
利用vue的脚手架vue-cli将项目配置好之后,如果做的 是移动端项目,需要在项目的根目录下的index.html的<heada>标签添加如下配置
<meta name="viewport" content="width=device-width,inittial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
页面宽度为移动端视口宽度,最小最大的页面大小为视口的大小,不能缩放
不同浏览器的默认css不一样,需要reset.css设置同样的css
1)在src/assets文件夹下新建文件夹styles,在建一个reset.css文件
2)将 https://meyerweb.com/eric/tools/css/reset/index.html 该内容粘贴在reset.css中
3)将reset.css引进项目中:在src/main.js文件的头部添加如下代码
import './assets/styles.reset.css'
当页面中的样式有所改变的时候,证明该文件引入成功
移动端用1px问
在vue中使用css预处理器stylus:
cnpm install --save stylus
cnpm install --save stylus-loader
在vue的组件中使用stylus语法书写css
//scoped使得当前组件的样式只对该组件生效,不会影响到别的组件
<style scoped lang='stylus'>
</style>
移动端的布局,一般使用rem,rem的计算是相对于根元素html中设置的font-size的大小进行计算的
html{
font-size:50px
}
如果p的width是43px,那么有
p{
width:.86rem
}
(43 * 2)/(50*2)=0.86rem,这就是设置html的font-size为50px的原因,便于计算
在组件中使用icon-font
1)在https://www.iconfont.cn官网上将要下载的图标添加进购物车,然后添加进项目,然后下载到本地,将字体库和.css文件复制到项目中的文件中,在main.js中将.css文件引进来
2)在组件中,直接使用该类,内容为其16进制代码
在webpack.base.conf.js中设置解析规则,使得引进变量的路径变得简短些
如下面:
<style lang="stylus" scoped>
@import '@/assets/styles/varibles.styl'
</style>
可以将路径写成:
@import '~styles/varibles.styl'
使用stylus中的变量-----背景色在很多页面共用同一个值,设置变量,便于管理项目,如果项目风格改变,易于管理,只需要改变一个地方即可
1)首先,在src/assets/styles下创建文件varibles.styl
2)在上述文件中添加内容,如上图所示
3)在组件中使用该变量
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.header
display:flex
background-color:$bgColor
</style>
这样就可以了
在实际的项目当中,每添加一个新的功能就会创建一个新的分支,当代码完成之后会将代码合并到主分支上
安装时--save和--save-dev的区别
在package.json中有两个属性,dependencies和devDependencies
dependencies-表示运行时、发布到生产环境时还需要依赖的插件,--save表示下载的模块会安装到dependencies
devDependencies表示开发、打包时需要的插件,--save-dev下载的模块会安装到devDependencies
npm install '模块名'-----将模块下载到node_modules中
详解 https://blog.csdn.net/cc18868876837/article/details/81109664
在vue 中创建轮播图,使用插件vue-awesome-swiper
1)在项目中安装Vue-awesome-swiper,由于最新版有问题,可以在安装的时候指定版本安装
cnpm install vue-awesome-swiper@2.6.7 --save
2)在main.js中将vue-awsome-swiper引进来,并通过Vue.use(VueAwesomeSwiper)使用该插件,如下图所示
3)在组件中使用轮播图
在使用轮播图时,存在抖动问题,解决方法:链接 https://blog.csdn.net/tangxiujiang/article/details/88872444
4)设置轮播图中的分页符:设置属性swiperOption中的pagination
修改分页符的背景色---->>>样式穿透,样式不受scoped的限制
修改轮播图组件中的样式:使用>>>来改变
页面图标的拖动,即轮播图,每页显示8个,当多于9-16个之后显示在第二页,以此类推
1)如下所示,是在同一张slider中显示,超过8个图标的部分会被隐藏掉,2)中会利用计算属性设置两页显示的效果
2)利用computed计算分页页数,实现两页显示----将数据分别用不同的数组存储在计算属性pages中
3)在<swiper-slide>中使用for循环,根据不同的页数,获取到数据----key值最好不要用index,不利于diff算法的性能优化
使用stylus中mixins功能------对于一些css样式,可能会多处用到,此时可以在src/assets/styles文件夹下建立一个mixins.styl文件
1)内容如下:
2)在组件的<style>中头部引入该文件
@import '~styles/mixins.styl'
3)在相应的位置使用该函数
.icon
ellipsis()
position:absolute
在 vue中使用axios发送ajax请求
1、首先安装
cnpm install --save axios
2、在static文件夹下创建静态数据,如static/mock/index.json
3、在.gitignore文件添加如下内容,使得代码提交的时候mock中的内容不被提交
4、在组件中的mounted调用axios发送ajax请求
import axios from 'axios'
export default {
name:'Home',components: {
HomeHeader,HomeSwiper,HomeIcons
},methods:{
getHomeInfo(){
axios.get('/static/mock/index.json')
.then(this.getHomeInfoSucc)
},getHomeInfoSucc(res){
console.log(res)
}
},mounted(){
this.getHomeInfo()
}
}
注意:axios.get('/static/mock/index.json')这里的路径是静态的,当提交到线上的时候是需要修改成'api/index.json',一般不建议上线前修改该路径,容易出错,存在风险
解决方法:如果生产环境使用'api/index.json',而开发环境使用'/static/mock/index.json',vue中提供了proxy的代理功能,通过该功能就可以实现上面的构想
A)在config/index.js文件中,对象属性dev中的proxyTable属性添加如下内容:当在开发环境下遇到以/api开头,则替换为‘/static/mock’
B)则axios请求的路径可以直接写成线上的路径了
getHomeInfo(){
axios.get('/api/index.json')
.then(this.getHomeInfoSucc)
}
用ajax获取到动态数据,渲染下图中的内容,左边是一个组件,右边字母是一个组件,实现页面布局
1)数据格式如下所示:数据在属性cities对象中,每个属性的值是一个数组
在v-for中如果数据cities是数组,使用形式为v-for="(item,index) of cities"
如果cities是对象,使用形式为v-for="(item,key) of cities"-------区别一个是index一个是key
2)左边内容对应的是List.vue组件部分内容如下----有嵌套的v-for,只要同层的key不一样就可以
cities是属性,所以外层for用key,item是数组,所以内层for用index
3)右边对应的是Alphabet.vue组件,cities是对象,其key就是A B C等
为上面的组件添加逻辑------兄弟组件间联动
父子组件进行传递数据:使用$emit()
兄弟组件间联动
可以使用的方法有:
1)Vuex-----不同页面之间共享数据可以使用这种方法
2)子组件通过$emit()传给父组件,父组件在传给另外一个子组件,另外一个子组件通过props接收------同一个页面的不同组件可以使用这种方法----兄弟组件进行传递数值使用这种方法比较方便、简单(这里用这种方式)
3)eventBus事件总线
点击右边的字母,跳到左边相应的字母中
1)在组件Alphabet.vue给<li>添加点击事件
当点击右边字母的时候,获取到e对象,通过e.target.innerText获取到该字母内容,然后通过$emit()将数据传递给父组件
2)在父组件City.vue中监听子组件Alphabet.vue中change事件
3)在父组件City.vue的data中定义letter属性默认值为空,然后在methods中定义handleLetterChange事件,当触发change事件时,就把获取到的数据赋值给this.letter,然后在子组件List.vue实例上绑定属性传递给子组件List.vue
data(){
return {
letter:''
}
},methods:{
handleLetterChange(letter){
this.letter = letter
}
}
4)在子组件List.vue中接受父组件传递过来的数据letter
5)此时子组件List.vue就接收到父组件City.vue传递过来的数据了,这时候需要通过watch对letter做一个监听,当其值发生变化的时候,就需要将点击右边的列表项中的字母与左边列表中的字母对应项显示出来
在DOM中绑定属性:ref="key",因为cities是对象,key的值对应的就算A B C,
然后js通过watch监听父组件City.vue传过来的letter,当letter有变化的时候,通过element = this.$refs[this.letter][0]获取到字母对应的DOM元素,然后调用better-scroll中的函数this.scroll.scrollToElement(element),将页面滚动到该元素位置
注意:由于DOM中是用v-for来书写DOM,所以通过this.$refs[this.letter]获取到的是一个只有一个元素的数组,所以写成this.$refs[this.letter][0]
这样就实现,点击左边字母,右边跳转到相应的字母的地方
功能-----拖动右边的字母表的时候,左边的字母也能跟着一起滚动
1)需要对右边字母表做滚动事件监听,在子组件Alphabet.vue中的li绑定事件touchstart touchmove touchend
2)希望在touchstart之后再触发touchmove之后的内容,在data中定义标志类touchStatus,默认为false,在手指触摸开始之后修改其值为true,在结束touch之后,修改其值为false,只有在touchStatus为true时,才做一些move的处理
在右边字母上下滑动的时候,要获得现在处于第几个字母
思路:获得右边第一个字母距离页面顶部的距离,滑动的时候获得手指距离顶部的高度,两者做一个差值获得当前手指与第一个字母的距离,然后除以每一个字母的高度,就可以获得当前第几个字母了,获得该字母之后,触发change事件,将数据传递给外部
1)在 子组件Alphabet.vue中定义计算属性数组letters,因为cities是对象,其属性key就是A B C.....,需要将key作为计算属性数组letters的值
2)可以将DOM 中的v-for数据改为用letters,每一个li用一个ref,用于在JS中获取到该元素
3)当处于滑动的时候,通过 startY = this.$refs['A'][0].offsetTop获取到字母A距离绿色底部的距离(因为li用v-for书写的,所以this.$refs['A'])是一个只有一个元素的数组
通过 e.touches[0].clientY 获取到手指距离 页面顶部的距离,touchY = e.touches[0].clientY - 绿色部分的高度,就是手指距离绿色底部的距离
每个字母高度是20,将index=Math.floor((touchY-startY)/20),就是手指指向数组letters里面对应的字母,然后通过this.$emit('change',this.letters[index])将当前字母传递给父组件
图一
4)在父组件已经定义change事件了,就可以实现滚动了,点击右边的字母,跳到左边相应的字母中---有写跳转的功能
列表切换性能优化
1)如图一所示的handleTouchMove(e)中的代码
问题:由于这个值是固定的,而每次执行这个方法的时候,都会运算一次,所以性能比较低
解决方法:在data中定义变量startY=0,在updated中更新该值this.startY=this.$refs['A'][0].offsetTop,当页面数据更新完成和页面渲染完成之后updated才执行,所以性能得到提升
图一的handleTouchMove(e)改写为:
2)代码性能:函数节流----当拖动右边的字母的时候,该函数handleTouchMove的执行频率是非常高的,通过函数节流,限制函数执行的频率---即将函数延迟一会在执行,如果在这次滑动还没有结束,又有一个滑动,那么就会清除上次滑动,触发当前滑动
在data中定义timer=null,然后改写handleTouchMove,当this.timer存在时则清除掉,否则创建timer,就执行代码放在定时器setTimeout()里面
实现搜索功能-----点击搜索框,输入城市名或者拼音的时候,能把搜索到的结果显示出来
1)Search.vue中DOM的内容
.search-content的布局如下:search-content用来显示搜索的结果
2)在input中对数据做一个绑定,data中定义keyword='',在 input中通过v-model和keyword做一个双向绑定
3)Search.vue中接受Search.vue实例传过来的数据,在父组件City.vue中定义cities
4)在子组件Search.vue中的props接收父组件City.vue传递过来的cities,并在data里面定义空数组list=[]用于存放和关键词一样的结果项,timer=null(函数节流用的)
5)在watch里面对keyword做一个监听,并且做一个函数节流的优化,数据结构如下
关键词和spell或者name匹配上,就放进result数组中
6)获取到列表项this.list,就可以在DOM中做v-for循环
到此,在搜索框输入关键词就可以在页面上显示出匹配的搜索内容了
对.search-item的css如下:
问题一:搜索出来的匹配项过多,一页显示不完全,可以使用better-scroll
DOM修改内容如下:
在mounted()中生成better-scroll实例对象
此时,多出的部分,通过滚动就可以显示出来了
问题二:当清空搜索框的关键词时,搜索结果还显示出来----实际上应该被清空
在对keyword做watch时,做一个判断,this.keyword为空时,将list=[],然后return掉
问题三:当没有匹配项的时候,页面没有任何提示
在ul中添加一个li,内容为没有找到匹配数据
问题四:这样书写,当有或者无数据的时候,这个li一直有显示
可以定义一个计算属性来控制其显示与否
DOM内容改成下面的形式:
问题五:这样书写,没有找到匹配数据的li一直显示,将城市选择一直覆盖掉
用keyword控制content-search的显示,当keyword有值的时候则显示search-content的内容,无则不显示
在路由中使用ajax获取数据---使用url中的查询参数
mounted(){
this.getDetailInfo()
},methods:{
getDetailInfo(){
// 将解析到的参数值放到id里面
axios.get('/api/detail.json',{
params:{
id:this.$route.params.id
}
}).then(this.handleGetDataSucc)
},handleGetDataSucc(res){
res= res.data
console.log(res)
}
}
使用vuex实现非兄弟组件之间的数据共享
功能-----点击City.vue中的城市之后,在首页的顶部显示对应的地点---也就是城市选择的数据由传递给首页
1)先安装Vuex在本地项目上,然后在src下创建store文件夹,在store下创建index.js,内容如下,有一一个公用数据city,默认值是北京
2)在src/main.js中将store引进来
3)在组件中使用引进来的store,在Header.vue中的DOM内容,通过{{this.$store.state.city}}获取到公用数据
4)City.vue中的当前城市,应该也是store中state.city的值
List.vue中的内容如下
5)当点击热门城市的时候,当前城市和首页右上角的城市会跟着变化
a)在List.vue中给每一个热门城市绑定点击事件---在组件中派发事件this.$store.dispatch(action,data)
b)在store/index.js中创建changeCity-----action用于commit mutation的
c)在store中创建mutations,用于修改state
问题:当选择好了热门城市,比如‘三亚‘’,当刷新页面,又变成默认值了,而不是‘三亚’
解决方法:使用localStorage----在Vue中直接使用该localStorage对象
在mutations中的changeCity修改state的时候,将city存储为localStorage
而city的默认值分两种情况:当localStorage对象中有city时使用localStorage.city的值,否则为上海
问题:如果使用localStorage最好使用try...catch,以防出现页面报错的情况
将上面的代码修改为
将src/store/index.js中的代码进行拆分
1)在src/store下新建state.js,内容如下
2)在src/store/index.js中将state引进来
import state from './state.js'
3)在src/store/mutations.js文件中内容如下
4)在src/store/index.js中将mutations引进来
import mutations from './mutations.js'
代码的优化---在DOM中直接使用this.$store.state.city获取到数据,而是在computed中使用...mapState()来获取到数据
在methods中通过...mapMutations()将store中的action映射成component的methods
使用keep-alive优化网页 性能
问题:当前代码存在的一个问题就是,每次路由切换的时候,都会发送一次ajax请求(因为每次进入一个页面的时候,mounted都会被重新执行一次,ajax就会被执行),造成性能很低
1)在App.vue中给<router-view>添加<keep-alive>,路由的内容被加载一次之后,就把路由中的内容放到内存中,下一次进入路由中不需要重新渲染这个组件
2)在首页中,选择'桂林'时,页面显示的是桂林的内容,‘北京’时显示的是北京的内容,,,即当地点发生变化的时候,需要重新发送一次ajax请求
a)在Home.vue中发送ajax请求的时候,路由中的路径带一个参数city,即
b)在Home.vue中data定义一个lastCity数据
c)在挂载的时候,对lastCity赋值
d)当页面被重新激活的时候,在activated中书写逻辑功能,在Home.vue中的生命周期函数activated做一些处理------当前页面的城市和上一次页面显示的城市是否相同,不相同发一次ajax请求,并对lastCity进行更新
对全局事件解绑---本意是在其中一个固定的组件中,响应某一个特定的事件,结果在别的组件中也响应该事件,因为该事件绑定在全局中,并不是在某一个特定的组件中绑定
activated(){
// 这个是全局事件
window.addEventListener('scroll',this.handleScroll)
},methods:{
handleScroll(){
const top = document.documentElement.scrollTop
if(top>60){
let opacity = top/140
opacity =opacity>1?1"opacity"
this.opacityStyle = {opacity}
this.showAbs = false
}else{
this.showAbs = true
}
}
}
在组件中添加声明周期函数deactivated,在里面对事件进行解绑
activated(){
// 这个是全局事件
window.addEventListener('scroll',deactivated(){
// 当页面切换到别的页面时候,解绑该事件
window.removeEventListener('scroll',methods:{
handleScroll(){
const top = document.documentElement.scrollTop
if(top>60){
let opacity = top/140
opacity =opacity>1?1"opacity"
this.opacityStyle = {opacity}
this.showAbs = false
}else{
this.showAbs = true
}
}
}
递归组件------在组件的自身里面调用组价自身,name属性的作用,通过使用自己的名字来使用自己
数据形式,一般会有一个children属性
// 数据形式,一般包含children属性
{
list: [{
title:'成人票',
children: [{
title:'成人三馆联票'
]}
},{
title:'学生票',
}]
}
调用形式
<template>
<div>
<div
class="item"
v-for="(item,index) of list"
:key = "index"
>
<div class="item-title border-bottom">
<span class="item-title-icon"></span>
{{item.title}}
</div>
<div v-if="item.children">
// 自身调用自身,这就是name属性的作用
<detail-list :list="item.children"></detail-list>
</div>
</div>
</div>
</template>
<script>
export default{
name:'DetailList'
}
</script>
vue项目中的接口联调(即页面中请求的地址是线上的地址)
1)联调的时候,将项目根目录下的static/mock文件夹删掉,访问的是服务器中的文件,需要开启一个后台php服务器
2)在config/index.js中将proxyTable中的内容改成下面的形式
module.exports = {
dev:{
assetsSubDirectory:'static',assetsPublicPath:'/',proxyTable:{
'/api':{
// 内网的地址,或者域名
// 不需要使用fiddler charles
target:'http://localhost:80'
}
}
}
}
vue项目真机测试(可以在手机上输入ip地址进行页面的访问)
1)获取当前计算机的ip地址:
ipconfig
问题:在地址栏中输入:ip:8080页面显示拒绝访问,而localhost:8080却能显示,如果将端口换成80那么:ip:80,页面可以显示
地址栏输入ip:8080页面显示拒绝访问的原因:项目是通过webpack-dev-ser启动的,webpack-dev-server默认不通过ip的形式进行页面的访问,所以需要对默认配置进行修改
2)解决方法:在项目根目录下,打开package.json文件,在scripts属性中的dev添加内容
--host 0.0.0.0
3)重启项目:在地址栏中通过ip地址进行访问:ip:8080就可以访问到页面中的内容了
这时候可以用手机通过内网,通过ip地址访问网页
4)滚动字母表的时候,整个页面跟着滚动:这时候可以在touchstart中添加修饰符prevent
@touchstart.prevent="handleTouchStart"
5)在低版本的android中,可能存在页面白屏的现象(10-2)
vue项目的打包上线
1)在cmd中进入项目的目录
2)执行:npm run build------对src下的代码进行打包和编译,生成一个压缩后的能被浏览器识别的代码
3)打包完成之后,在项目根目录下会多出dist(index.html + static文件夹)文件夹,而dist目录中的内容就是要上线的内容
4)将dist目录中的代码放到后端服务器上的根目录下
5)此时在地址栏中输入localhost就可以直接输出网页的内容了
以上就是简单的项目打包上线过程
如果将打包出来的文件(index.html + static文件夹)放在后端服务器的根目录project文件夹中,然后在地址栏中输入localhost/project访问页面,需要在后端文件config/index.js文件进行配置
原文件内容如下:
1)将assetsPublicPath中的内容改成如下所示:修改文件路径
assetsPublicPath:'project'
2)在cmd中重新打包:npm run build
3)将dist中的内容复制到服务器根目录下的project文件夹里面,就可以了