Vue核心要点理解
Vue性能操作
- computed计算属性的缓存和methods,watch
- v-if和v-show
- :key 解决唯一性,循环遍历的时候最好用数据的id,而不是下标
- 使用v-once来提高动态组件之间的切换,是组件第一次渲染之后放在内存中,后面操作直接调用,提高性能
- 函数节流
- 使用keep-alive结合钩子函数activated进行页面性能优化
Vue的数据传递
- 父传子 通过v-bind:传递,子组件通过props接收
- 子传父 子组件通过$emit发送事件,父组件通过v-on监听事件
- 非父子 通过bus/发布订阅/总线机制来传递,或者使用vuex传递
- 父传子可以通过slot插槽传递数据
MVVM和MVC
- MVVM:M表示数据model,是获取到的数据。V表示视图view,是展示的界面。VM表示视图模型,用来连接model和view,是vue底层自动实现,不需要认为操作。MVVM是数据驱动的。
- MVC: M表示数据model,是从后台用ajax获取到的数据。V表示视图层view,是前端渲染的页面。C表示控制器controler,用来将获取到的数据使用dom操作来更新页面,VMMC中数据和操作dom是混合的,controler控制器部分比较庞大,比如原始的jquery就是MVC模式。
虚拟DOM和Object的defineProperties
前端的组件化
父子组件之间的传值
- 父传子
- 使用
v-bind:属性名=变量值
传递 - 使用
props: [属性名]
接收
- 使用
- 子传父
- 子组件通过事件使用
this.$emit(事件名,数据)
发送事件和数据 - 父组件通过
v-on:事件名="父组件事件"
监听并接收传递过来的数据
- 子组件通过事件使用
Vue的实例
vue的每一个组件都是一个vue的实例。
vue的项目都是用多个vue实例拼装而成的。
Vue的生命周期
生命周期函数就是vue实例在某个事件点会自动执行的函数
beforeCreate: 在此之前只有默认的一些事件和生命周期钩子函数
created: 在此过程中,data和methods都已经被初始化好了,最早可以在created中操作方法和数据
beforeMount: 表示模板已经在内存中编译完成了,但是尚未渲染到页面中,页面中的元素还没有替换过来,还是之前的模板字符串,页面数据还未编译到模板template中
mounted: 内存中的模板已经挂在到了页面中,实例已经被完全创建好了
beforeUpdate: 页面中的数据被更新了,此时data中的数据是最新的,页面尚未和最新的数据保持同步。
updated: 更新之后,页面和data中的数据保持同步,都是最新的。
beforeDestroy: 当调用了vm.$destroy()函数的时候触发
destroyed: 当vue实例被销毁的时候触发
计算属性
计算属性是基于它们的依赖进行缓存的,如果依赖的值没有发生改变,计算属性也不会执行
相对于watch监听和方法来说,同样的功能,计算属性相对来说比较简单而且有缓存
计算属性默认调用的是get,还可以设置set.
watch监听,和compute计算属性类似,也具有缓存。只有依赖发生改变时候才会触发,但是写法比较麻烦
get,set的使用
样式绑定
class
-
对象绑定
:class={类名:变量}
-
数组绑定
:class=[变量]
style
-
对象绑定
:style={变量}
-
数组绑定
:style=[变量]
iff算法
条件渲染
v-if: 每一次都会删除和添加dom,性能不好
v-show: 经常操作的可以使用v-show,性能好
问题:
# 解决vue的复用问题,在不添加key的时候,当切换显示和隐藏时候,vue会复用文本框,里面内容也不会改变,解决办法就是给元素加一个key就可以使之唯一,vue就会重新渲染。
<div id="app">
<div v-if="isShow">
用户名:<input type="text" key="username">
</div>
<div v-else>
密码:<input type="password" key="password">
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
isShow: true,
}
})
</script>
vue数组的变异方法
Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。这些方法如下
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
改变数组内容的方法:变异方法和引用(list = [])、set方法(Vue.set(vm.userInfo,1,5))
改变对象内容的方法:引用(obj = {})、set方法(vm.$set(vm.userInfo,‘address’,‘beijing’))
template标签的使用
用来占位,包裹一些元素,但是不显示
使用is解决组件模板标签的bug问题
<div id="box">
<table>
<tbody>
<!-- <row></row> -->
<tr is="row"></tr>
<Ul>
<li is="row"></li>
</Ul>
</tbody>
</table>
</div>
<script>
Vue.component('row', {
template: '<tr><td>this is a row</td></tr>'
})
var vm = new Vue({
el: '#box'
})
</script>
子组件中定义data必须是函数
<div id="box">
<table>
<tbody>
<tr is="row"></tr>
</tbody>
</table>
</div>
<script>
Vue.component('row', {
data: function(){
return {
content: 'this is a row'
}
},
template: '<tr><td>{{content}}</td></tr>'
})
var vm = new Vue({
el: '#box'
})
</script>
# 作用:是为了让每个可复用的组件拥有自己的独立数据
使用ref来获取dom
# 在html元素上的ref可以获取dom
<div id="box">
<div ref="hello" @click="handleClick">hello world</div>
</div>
<script>
var vm = new Vue({
el: '#box',
methods: {
handleClick: function(){
alert(this.$refs.hello.innerHTML)
}
}
})
</script>
# 使用发布订阅的方式,在组件上的ref可以获取该组件的引用
<div id="box">
<counter ref="one" @change="handleChange"></counter>
<counter ref="two" @change="handleChange"></counter>
<div>{{total}}</div>
</div>
<script>
Vue.component('counter', {
template: '<div @click="handleClick">{{number}}</div>',
data: function(){
return {
number: 0
}
},
methods: {
handleClick: function(){
this.number ++
this.$emit('change')
}
}
})
var vm = new Vue({
el: '#box',
data: {
total: 0
},
methods: {
handleChange: function(){
this.total = this.$refs.one.number + this.$refs.two.number
console.log(this.$refs.one)
}
}
})
</script>
父子组件之间的数据传递
vue中的单项数据流,只允许父组件向子组件传递数据,不允许子组件改变父组件传递过来的数据。如果需要修改父组件传递过来的数据,则可以将传递过来的数据保存为自己的变量,然后做更改。
组件参数校验
props: {
content: String,
age: Number,
id: [Number, String],
address: {
type: String,
required: true,
default: 'default value',
validator: function(value){
return value.length > 5
}
}
}
非props属性
- 非props特性:当父组件向子组件传递数据时候,子组件并没有用props接收;在子组件中无法使用
- 非props特性传递给子组件的属性及属性值会显示到dom中html的属性中
给组件绑定原生事件
在组件上绑定事件的时候,这个事件是属于监听事件,而不是原生事件。因此当点击时候不会触发。如果需要触发,有以下两种操作:
- 通过子组件使用事件$.emit(‘事件名’),发送一个事件,然后父组件监听@事件名='自己的事件’来触发
- 通过事件修饰符,只需要在子组件上定义的事件后面加上一个.native.
@click.native="chandleClick"
非父子组件间的传值
通过(Bus/总线/发布订阅模式/观察者模式)
- 非父子组件之间数据传递的方式
- 逐层传递
- 使用Vuex
- 使用发布订阅模式总线机制传递
<div id="box">
<child content="Dell"></child>
<child content="Lee"></child>
</div>
<script>
Vue.prototype.bus = new Vue()
Vue.component('child', {
props: {
content: String
},
data: function() {
return {
selfContent: this.content
}
},
template: '<div @click="handleClick">{{selfContent}}</div>',
methods: {
handleClick: function() {
this.bus.$emit('change', this.selfContent)
}
},
mounted: function() {
var that = this
this.bus.$on('change', function(msg) {
that.selfContent = msg
})
}
})
var vm = new Vue({
el: '#box'
})
</script>
Vue中的插槽
在vue中,父组件向子组件传递数据的时候,通过绑定属性向子组件传递,子组件通过props来接收
通过插槽的方式可以批量的在子组件内部传递数据,然后通过slot来接收,并且可以接收具名slot内容。
<div id="box">
<body-content>
<div slot="header">header</div>
<div slot="footer">footer</div>
</body-content>
</div>
<script>
Vue.component('body-content', {
template: `
<div>
<slot name="header"></slot>
<div>content</div>
<slot name="footer"></slot>
</div>
`
})
var vm = new Vue({
el: '#box'
})
</script>
Vue中的作用域插槽
<div id="box">
<child>
<template slot-scope="props">
<h1>{{props.item}} - hello</h1>
</template>
</child>
</div>
<script>
Vue.component('child', {
data: function() {
return {
list: [1, 2, 3, 4]
}
},
template: `
<div>
<ul>
<slot v-for="item of list" :item='item'>
</slot>
</ul>
</div>
`
})
var vm = new Vue({
el: '#box'
})
</script>
动态组件
<div id="box">
<component :is="type"></component>
<!-- <child-one></child-one>
<child-two></child-two> -->
<button @click="handleBtnClick">动态组件</button>
</div>
<script>
// v-once 减少性能的消耗
Vue.component('child-one', {
template: '<div v-once>child one</div>'
})
Vue.component('child-two', {
template: '<div v-once>child two</div>'
})
var vm = new Vue({
el: '#box',
data: {
type: 'child-one'
},
methods: {
handleBtnClick: function() {
this.type = this.type === "child-one" ? "child-two" : "child-one";
}
}
})
</script>
在Vue中 使用animate.css库
<div id="box">
<transition enter-active-class="animated swing" leave-active-class="animated shake" appear appear-active-class="animated shake">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
在Vue中同时使用过渡和动画
<style>
.fade-enter, .fade-leave-to {
opacity: 0
}
.fade-enter-active, .fade-leave-active {
transition: opacity 1s
}
</style>
<transition
type=""
:duration="{enter: 5000, leave: 10000}"
name: "fade"
appear
enter-active-class="animated swing fade-enter-active"
leave-active-class="animated shake fade-leave-active"
appear-active-class="animated swing"
>
<div v-show="show">
hello world
</div>
</transition>
Vue动画中属性声明javascript钩子
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
<!-- ... -->
</transition>
Vue中使用Velocity.js
中文文档:http://www.mrfront.com/docs/velocity.js/index.html
Vue中多个元素/组件的过渡和动画
vue中的dom的复用,导致动画不会出现,需要添加不同的key值
<style>
.v-enter,v-leave-to{
opacity: 0
}
.v-enter-active, .v-leave-active{
transition: opacity 1s;
}
</style>
<transition mode="in-out/out-in">
<div v-if="show" :key="A">
hello world
</div>
<div v-else :key="B">
bye world
</div>
</transition>
Vue中的列表过渡
<style>
.v-enter,v-leave-to{
opacity: 0
}
.v-enter-active, .v-leave-active{
transition: opacity 1s;
}
</style>
<transition-group>
<div v-for="(item, index) of arr">
{{item.title}}
</div>
</transition-group>
Vue中动画的封装
<div>
<fade :show="show">
<div>
hello world
</div>
</fade>
<button @click="handleBtnClick">
toggle
</button>
</div>
Vue.component('fade', {
props: ['show'],
template: `
<transition @before-enter="handleBeforeEnter"
@enter="handleEnter"
>
<slot v-if="show"></slot>
</transition>
`,
methods: {
handleBeforeEnter: function(el){
el.style.color="red"
},
handleEnter: function(el, done){
setTimeout(() => {
el.style.color = 'green'
done()
}, 2000)
}
}
})
Vue项目开发环境
- 安装node.js
- 检测npm -v和 node -v
- 创建码云的仓库
- 用账号登录码云
- 然后选择项目>Private点击+号创建私有的项目
- 依次填写项目名称>项目介绍>是否公开(私有)>选择语言(JavaScript)> 添加开源许可证(MIT License)> 使用Readme文件初始化这个项目>创建即可
- 安装git并使用git将本地仓库和远程仓库进行关联
- 点击码云头像>设置>SSH公钥>项目管理>公钥 管理>生成/添加SSH公钥
- 在git-bash中三次回车执行
ssh-keygen -t rsa -C "xxx@qq.com"
,如果之前生成过公钥,这会提示已经生成,不需要再次生成了 - 然后在 git-bash中执行
cat ~/.ssh/id_rsa.pub
获取公钥 - 然后将公钥添加到SSH公钥的里面,标题会自动生成。
- 这样在本地会有一个私钥,然后私钥和公钥匹配之后就可以不用密码来上传和更新。
- 配置SSH私钥并建立连接
- 将线上的仓库通过SSH克隆到本地。在本地执行
git clone SSH地址
- 安装vue脚手架工具
npm install --global -vue-cli
- 在克隆下来的文件夹中使用vue-cli创建vue项目初始化项目:
vue init webpack projectName(项目名)
**ps:**注:项目名不能大写,不能使用中文 - 进入项目文件夹,执行
cnpm install
下载依赖 - 运行 npm run dev
- 在项目文件夹中执行
git add .
和git commit -m ''
提交到本地仓库 - 然后执行
git push
将本地库提交到码云仓库
详细的环境配置:https://blog.youkuaiyun.com/yw00yw/article/details/81201670
单页应用VS多页应用
- 多页应用网站
- 当每次请求跳转的时候后台都会返回一个新的html文件,则为多页应用网站
- 优点:首屏时间快,SEO效果好
- 缺点:页面切换慢
- 当每次请求跳转的时候后台都会返回一个新的html文件,则为多页应用网站
- 单页应用网站
- 页面每次跳转并不加载html页面,而是通过js动态清除当前页面内容然后渲染新的html页面内容,只请求加载了一次。
- 优点:页面切换快
- 缺点:首屏时间稍慢,SEO差
- 页面每次跳转并不加载html页面,而是通过js动态清除当前页面内容然后渲染新的html页面内容,只请求加载了一次。
项目初始化
- 增加meta标签
样式问题——在main.js中引入
- 引入reset.css使各个浏览器显示的效果一样
import './assets/styles/reset.css'
- 引入border.css解决移动端1px问题
import './assets/styles/border.css'
js问题
- fastclick 解决移动端click300ms延迟问题
import fastClick from 'fastclick'
fastClick.attach(document.body)
第三方字体库
- iconfont
css预处理器
stylus——> npm install stylus stylus-loader --save
<style lang="stylus" scoped>
.header
height value
</style>
rem的使用
# 在rest.css中设置了html的根字体大小为 50px
# 因为设计稿一般是750px是iphone6的二倍图
# 那么可以简单在设计稿上量取的高度/宽度 = html * 2 = 50 * 2 = 1rem / 100
# 即 1px = 0.01rem
配置文件路径的别名,简化路径
build > webpack.base.conf.js中配置
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
'styles': resolve('src/assets/styles'),
}
}
引入
# 在css中引入必须加~
# 在main.js中引入不需要加前面的~
@import '~styles/stylus/varibles.styl'
分支的创建
# 在码云git仓库中创建一个分支,并起用分支名,然后将线上仓库使用`git pull`拉取到本地,然后使用`git checkout ...`切换到新分支上,然后进行开发,该功能模块开发完成之后依次提交到本地仓库`git commit`、线上仓库`git push`的当前分支上,然后切换到主分支,使用`git merge ...`合并到主分支,然后`git push`提交到远程仓库
轮播插件
使用Vue-Awesome-Swiper轮播插件
npm install vue-awesome-swiper --save
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper'
// require styles
import 'swiper/dist/css/swiper.css'
Vue.use(VueAwesomeSwiper, /* { default global options } */)
解决图片的宽高比例自适应问题,即图片的自适应,用来占位,防止页面的抖动
.wrapper
overflow: hidden
width: 100%
height: 0
padding-bottom: 31.25% /*图片的高宽比例*/
.swiper-img
width 100%
样式的穿透,使子组件的样式不受scoped的限制
.wrapper >>> .swiper-pagination-bullet-active
background #fff !important
使用swiper解决多页小图标轮播
computed: {
pages () {
const pages = []
this.iconList.forEach((item, index) => {
const page = Math.floor(index / 9)
if (!pages[page]) {
pages[page] = []
}
pages[page].push(item)
})
return pages
}
}
使用axios进行ajax数据的请求
- fetch
- vue-resource
- axios
npm install axios --save
在项目目录static目录下模拟json数据,因为只有static才可以在地址栏中请求到。
http://localhost:8082/static/mock/index.json
在.gitignore中写入不需要提交到仓库的文件
static/mock
- 则可以使用
axios.get('/static/mock/index.json').then(this.getHomeInfoSucc)
请求到数据
请求代理的替换,使用webpack提供的proxyTable解决跨域问题
打开项目根目录文件夹config下的index.js,然后在proxyTable属性中更改
proxyTable: {
'/api': {
target: 'http://localhost:8080', //目标接口域名
changeOrigin: true, //是否跨域
pathRewrite: {
'^/api': '/static/mock' //重写接口
}
}
}
请求:
axios.get('/api/index.json').then(this.getHomeInfoSucc)
父子组件之间的数据传递
将父组件中请求到的数据传递给所有子组件
移动端文字垂直居中问题
关于移动端文字无法垂直居中(或line-height不起作用)的问题的解决方案
使用better-scroll实现列表滚动
cnpm install better-scroll --save
<div class="wrapper">
<ul class="content">
<li>...</li>
</ul>
</div>
import BScroll from 'better-scroll'
const wrapper = document.querySelector('.wrapper')
const scroll = new BScroll(wrapper)
获取源事件的元素
handleLetterClick (e) {
console.log(e.target.innerText)
}
使用scrollToElement实现滚动
# 同级组件1
handleLetterClick (e) {
this.$emit('change', e.target.innerText)
}
# 同级组件2
watch: {
letter () {
if (this.letter) {
const element = this.$refs[this.letter][0]
this.scroll.scrollToElement(element)
}
}
}
触摸滑动
<div class="list">
<ul>
<li class="item" v-for="(city, key) of cities" :key="key"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
@click="handleLetterClick"
>{{key}}</li>
</ul>
</div>
data () {
return {
touchStatus: false
}
},
computed: {
letters () {
const letters = []
for (let i in this.cities) {
letters.push(i)
}
return letters
}
},
methods: {
handleLetterClick (e) {
this.$emit('change', e.target.innerText)
},
handleTouchStart () {
this.touchStatus = false
},
handleTouchMove (e) {
// 1. 获取A元素距离顶部的距离
// 2. 获取手指滑动距离顶部的当前距离
// 3. 相减得到手指滑动到字母的距离
// 4. 使用在字母上得到的距离/字母的高度=字母下标
const startY = this.$refs['A'][0].offsetTop
const touchY = e.touches[0].clientY - 79
const index = Math.floor((touchY - startY) / 20 )
if (index >= 0 && index < this.letters.length) {
this.$emit('change', this.letters[index])
}
},
handleTouchEnd () {
this.touchStatus = true
}
}
列表性能优化
- 函数节流
if (this.touchStatus) {
if (this.timer) {
clearTimeout(this.timer)
}
this.timer = setTimeout(() => {
const touchY = e.touches[0].clientY - 79
const index = Math.floor((touchY - this.startY) / 20 )
if (index >= 0 && index < this.letters.length) {
this.$emit('change', this.letters[index])
}
}, 16)
}
搜索功能 实现
watch: {
keyWord () {
if (this.timer) {
clearTimeout(this.timer)
}
if (!this.keyWord) {
this.list = []
return
}
this.timer = setTimeout(() => {
const result = []
for (let i in this.cities) {
this.cities[i].forEach(value => {
if (value.spell.indexOf(this.keyWord) > -1 || value.name.indexOf(this.keyWord) > -1) {
result.push(value)
}
})
}
this.list = result
}, 100)
}
},
mounted () {
this.scroll = new BScroll('.search-content')
}
使用vuex实现数据管理
export default new Vuex.Store({
state: {
city: '北京'
},
getters: {},
actions: {
changeCity(ctx, city) {
ctx.commit('changeCity', city)
}
},
mutations: {
changeCity(state, city) {
state.city = city
}
}
})
使用localStorage实现切换不同城市的数据存储,实现城市保存
let defaultCity = '北京'
try {
if (localStorage.city) {
defaultCity = localStorage.city
}
} catch (e) {}
export default new Vuex.Store({
state: {
city: defaultCity
},
getters: {},
actions: {
changeCity(ctx, city) {
ctx.commit('changeCity', city)
}
},
mutations: {
changeCity(state, city) {
state.city = city
// 保存城市
try {
localStorage.city = city
} catch (e) {}
}
}
})
使用keep-alive性能优化
每一次切换路由的时候,对应的ajax就会被执行请求,原因是因为执行了mounted钩子函数,正常情况下只需要获取一次,可以减少性能
keep-alive 包含的组件第一次执行的时候被放到内存中,下一次不需要请求,直接从内存中调用
<div id="app">
<keep-alive>
<router-view/>
</keep-alive>
</div>
activated
# 使用keep-alive会将请求的数据缓存到内存中,然后会调用activated钩子函数的执行,当页面改名的时候回执行该钩子函数。
activated () {
if (this.lastCity !== this.city) {
this.lastCity = this.city
this.getHomeInfo()
}
}
使用router-link或者动态路由实现页面详情
<router-link :to="'/detail/' + item.id" tag="li"></router-link>
{
path: '/detail/:id',
name: 'Detail',
component: Detail
}
在build目录下的webpack.base.config.js中配置别名
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
'styles': resolve('src/assets/styles'),
'common': resolve('src/common')
}
}
使用swiper配置图片的轮播
swiperOption: {
pagination: {
el: '.swiper-pagination',
type: 'fraction'
},
observer:true,
observeParents:true
}
采用监听事件scroll实现header显示隐藏及动画
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
}
}
使用activated 和deactivated 钩子函数对全局函数的添加和解绑
activated () {
window.addEventListener('scroll', this.handleScroll)
},
deactivated () {
// 对全局事件的解绑
window.removeEventListener('scroll', this.handleScroll)
}
使用递归组件实现多级列表数据展示
<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" class="item-children">
<detail-list :list="item.children"></detail-list>
</div>
</div>
使用activated 解决对keep-alive缓存问题的解决,让重新请求数据
或者使用以下属性解决
<keep-alive exclude="Detail"> # 组件名,除了detail组件之外的被缓存
<router-view/>
</keep-alive>
每一个组件的name的作用
- 在做递归组件的时候回用到
- 在页面上取消缓存时候会用到
- vue-devtools工具里面的组件名字
解决路由切换过程中滚动问题
# 在路由中添加
scrollBehavior(to, from, savedPosition) {
return { x: 0, y: 0 }
}
动画的封装和使用
FadeAnimation.vue
<template>
<transition>
<slot></slot>
</transition>
</template>
<script>
export default {
name: 'FadeAnimation'
}
</script>
<style lang="stylus" scoped>
.v-enter, .v-leave-to
opacity 0
.v-enter-active, .v-leave-active
transition opacity .5s
</style>
Banner.vue
<fade-animation>
<common-gallary :imgs="gallaryImgs" v-show="showGallary" @close="handleBannerClose"> </common-gallary>
</fade-animation>
Vue项目的接口联调
# config/index.js
proxyTable: {
'/api': {
target: 'http://localhost:80' //api地址可以换成内网的IP或者外网的域名
}
}
Vue项目的真机测试
# 获取当前电脑的内网IP
`ipconfig/all`
# 修改 package.json 文件(因为webpack的默认服务端口无法访问,只需要改成以下即可)
"dev": "webpack-dev-server --host 0.0.0.0 --inline --progress --config build/webpack.dev.conf.js"
# 解决低版本安卓机浏览器不支持promise,出现白屏问题
在项目目录下安装
'npm install babel-polyfill --save'
# 并且在main.js中添加
import 'babel-polyfill'
然后重启之后使用同局域网的IP地址加端口号访问
解决手机上的全屏滑动出现的问题
@touchstart.prevent="handleTouchStart"
Vue项目的打包上线
# 运行
`npm run build`
打包成功后为dist的文件夹
# 一般直接将打包后的文件index.html和static文件夹放到后端项目的根目录下
# 如果想放到指定的目录,则需要在 config/index.js中修改
build: {
assetsPublicPath: '/project'
}