转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥"前端一万小时"两大明星专栏------"从零基础到轻松就业"、"前端面试刷题",已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
plain
涉及面试题:
1. Vue 提供的事件修饰符是什么?
2. 什么是异步组件?
3. 异步组件工厂的结构是什么?
编号:[travel_25]
1 性能优化
项目进行到这里,我们整个项目的代码编写好像完成了。但其实,后续随着项目变得越来越庞大时,还会有一个可优化的"点"。
❌我们启动服务器,访问项目页面,打开控制台的 Network,选中 "JS",直接来观察一个问题:

从上边的视频中可以看到:当在首页刷新后,页面加载了 app.js
和 backend.js
两个 .js
文件。其中 app.js
,就是我们项目中所有页面的业务逻辑代码。所以,当我们从首页再进入详情页/城市选择页时,不会再次加载 JS 文件。
❓这就带来了一个问题:当我们访问首页时,需要一次性加载所有页面的 JS 代码吗?
答:目前我们项目还较小,所以这样的加载方式更好,完全不需要进行拆分(建议当打包后 app.js
至少大于 1MB 时进行异步加载)。
但假设项目变大,那么一次性加载所有页面的 JS 代码就会减低性能。此时,更好的方式就是使用"异步组件"来实现"按需加载"。即,当访问首页时,加载首页的 JS 代码;访问详情页时,再加载详情页的代码。
1️⃣打开 router 下的 index.js
:
javascript
import Vue from 'vue'
import Router from 'vue-router'
/* 1️⃣-①:之前我们在项目中直接引入组件后使用;
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
import Detail from '@/pages/detail/Detail'
*/
Vue.use(Router)
export default new Router({
routes: [{
path: '/',
name: 'Home',
// component: Home
component: () => import('@/pages/home/Home') /*
1️⃣-②:改为异步组件的形式,component 后
边不再是组件名,而是一个箭头函数,箭头函数
返回的值是 Home.vue;
*/
}, {
path: '/city',
name: 'City',
// component: City
component: () => import('@/pages/city/City') // 1️⃣-③:城市选择页改为异步加载;
}, {
path: '/detail/:id',
name: 'Detail',
// component: Detail
component: () => import('@/pages/detail/Detail') // 1️⃣-④:详情页页改为异步加载;
}]
})
保存后,返回页面查看。当我们在首页刷新后,会加载 app.js
、 backend.js
和 1.js
(即首页的 JS 代码);访问城市列表页/详情页时,仅会再加载对应页面的 .js
文件:
当然,除了可以在路由中使用异步组件的形式,只要是 Vue 中的组件,都可以进行异步加载。
例如:
html
<template>
<div>
<home-header></home-header>
<home-swiper :list="swiperList"></home-swiper>
<home-icons :list="iconList"></home-icons>
<home-recommend :list="recommendList"></home-recommend>
<home-weekend :list="weekendList"></home-weekend>
</div>
</template>
<script>
// import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
import { mapState } from 'vuex'
export default {
name: 'Home',
components: {
HomeHeader: () => import('./components/Header'), // ❗️异步加载首页的 Header 组件。
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
},
...略
}
</script>
2 联调、测试与打包上线
🔗前置知识:
《Node.js 与 NPM 入门:③ NPM 详解》
《首页开发------⑤ AJAX 获取首页数据》
2.1 前后端联调
之前我们编写代码时,实际所有 AJAX 请求的数据都不是后端返回的数据,而是我们自己模拟的假数据。
在实际工作中,当前端页面的代码已全部编写完毕,后端小伙伴也差不多编写完了相关的数据"接口"。
❓双方都编写好了代码,下一步我们前端应该怎么做呢?
答:这时我们就需要把 mock 的数据删除(删除 static 下的 mock 文件夹),去使用后端提供的数据,进行前后端的调试(即,前后端联调)。
在 Vue 中进行前后端联调非常方便,不需要使用类似于 Fiddler、Charles 之类的抓包代理工具。
只需要进行一些配置即可。比如,之前我们在 config 中的 index.js
配置过请求路径:
javascript
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
/*
❗️当请求 /api 这个路径时,把请求指向本地的 8080 端口,将以 /api 为开头的路径
替换为 /static/mock。
*/
'/api': {
target: 'http://localhost:8080',
pathRewrite: {
'^/api': '/static/mock'
}
}
},
...略
}
}
进行联调时,只需要将 taget
指向后台服务器的地址就可以了(如,内网 IP 地址或外网的域名等 )。端口默认为 80 端口,当使用的是 http
协议时,可以省略 :80
。
2.2 真机测试
当我们项目进行了联调之后,是不是就没有什么问题了呢?
当然不是,我们编写代码时,一直在浏览器运行。当项目运行在真机上后,有时会有一些编写过程中在浏览器上不会出现的 Bug。所以还需要进行"真机测试"。
❓Vue 项目中如何进行真机测试呢?
答:我们的项目启动时是通过 webpack-dev-server 启动的,而它默认不支持通过 IP 的形式访问页面。所以,我们需要修改它的默认项,然后在手机上通过内网的 IP 地址进行访问。
2️⃣打开 package.json
:
json
{
"name": "qdywxs-travel",
"version": "1.0.0",
"description": "A Vue.js project",
"author": "itsOli",
"private": true,
"scripts": { // 2️⃣-①:在 dev 中的 webpack-dev-server 后添加一个配置项 --host 0.0.0.0 ;
"dev": "webpack-dev-server --host 0.0.0.0 --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"lint": "eslint --ext .js,.vue src",
"build": "node build/build.js"
},
...略
}
2️⃣-②:在终端运行 ifconfig
命令(如果是 Windows 系统,则执行 ipconfig
),找到本机在内网中的 IP 地址;

2️⃣-③:运行 npm run dev
启动服务器,让手机与电脑在同一个局域网内,手机访问本机 IP 地址的 8080 端口;
(以下为 iPhone 6s Plus 机型录制)
通过上边视频可以看到,当手机访问项目时,页面正常显示。首页和详情页功能正常,城市选择页的基本功能正常。但,当手指在字母表滑动浏览城市时,整个页面跟着"滚动"了。
❓如何解决"滑动时,整个页面滚动"的问题呢?
答:我们只需要在代码中添加一个 Vue 中提供的事件修饰符.prevent
,用它来阻止 touchstart
的默认行为即可。
2️⃣-④:打开 city 下 components 中的 Alphabet.vue
;
html
<template>
<ul class="list">
<li
class="item"
v-for="item of letters"
:key="item"
:ref="item"
@click="handleLetterClick"
@touchstart.prevent="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
> <!-- 2️⃣-⑤:在 touchstart 事件上添加 .prevent 修饰符。 -->
{{item}}
</li>
</ul>
</template>
<script>
export default {
name: 'CityAlphabet',
props: {
cities: Object
},
computed: {
letters () {
const letters = []
for (let i in this.cities) {
letters.push(i)
}
return letters
}
},
data () {
return {
touchStatus: false,
timer: null,
startY: 0
}
},
updated () {
this.startY = this.$refs['A'][0].offsetTop
},
methods: {
handleLetterClick (e) {
this.$emit('change', e.target.innerText)
},
handleTouchStart () {
this.touchStatus = true
},
handleTouchMove (e) {
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)
}
},
handleTouchEnd () {
this.touchStatus = false
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.list
position: absolute
top: 1.58rem
right: 0
bottom: 0
width: .4rem
display: flex
flex-direction: column
justify-content: center
.item
line-height: .4rem
text-align: center
color: $bgColor
</style>
小伙伴们自己做真机测试时,因为手机型号不同,可能还会有一些其他的问题。比如,如果是低版本的安卓手机,可能会有手机访问项目出现"白屏"的效果。
产生这样的效果有两种情况:
- 手机浏览器默认不支持 Promise;
项目兼容性解决方案:项目目录下安装一个 babel-polyfill 第三方包(它会对浏览器进行判断,如果浏览器没有 Promise,它会自动帮助我们添加这些 ES6 的新特性);

然后在项目入口文件 main.js
中引入即可。
javascript
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import fastClick from 'fastclick'
import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'babel-polyfill' // ❗️引入 babel-polyfill。
import store from './store'
import 'styles/reset.css'
import 'styles/border.css'
import 'styles/iconfont.css'
import 'swiper/dist/css/swiper.css'
Vue.config.productionTip = false
fastClick.attach(document.body)
Vue.use(VueAwesomeSwiper)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
- 当引入 babel-polyfill 后重启项目依然白屏的话,其实不是代码的问题,而是 webpack-dev-server 的问题。解决方案:将项目打包好后,放在真正的开发环境或线上环境中。
2.3 项目上线
现在,我们的项目已经可以上线了。
❓直接把我们项目代码的 src 目录给到后端小伙伴将项目发布上线吗?
答:当然不是,我们在项目中编写的代码,必须经过"打包编译",生成一个能被浏览器运行的代码(代码在打包的同时会被压缩),才能交给后端小伙伴。
3️⃣终端定位到 qdywxs-travel
,运行:
plain
npm run build
3️⃣-①:当完成打包后,我们 qdywxs-travel
项目中会多出一个 dist
文件夹,这里边就是我们打包好的项目文件;
3️⃣-②:我们把 build
文件夹给到后端小伙伴即可;
3️⃣-③:后端小伙伴会将这个 build
文件夹里的所有文件拿出来放在后端工程 的 htdocs
目录下(这个目录里包含后端小伙伴编写的 api
接口文件)完成上线。
OK,青山不改,绿水长流,咱们下个项目再见~
祝好,qdywxs ♥ you!