vue最新版本:2.6
vue-cli:最新4.x
vue-devtools:5.3.3
二、统一用vue-cli 3.x脚手架写项目
vue-cli官网:https://cli.vuejs.org/zh/
安装vue-cli
npm install -g @vue/cli
创建项目
vue create 项目名
或
vue ui
查看vue脚手架的版本:vue -V
具体创建项目步骤:
第一步:vue create 项目名,然后按照步骤去执行
? Please pick a preset:
❯ default (babel, eslint) //回车自动安装脚手架需要的各种包
Manually select features //手动安装,需要自己配置选择各种要安装的包
第二步:安装好后进入项目目录并运行
$ cd 项目目录
$ yarn serve 或npm run serve //运行
vue-cli 3.x+通过vue.config.js来实现脚手架的环境配置
配置跨域选项:
module.exports = {
devServer: {
proxy: {
'/api': {
target: '<url>',
ws: true,
changeOrigin: true
},
'/foo': {
target: '<other_url>'
}
}
}
}
手机上测试项目
要保证手机和电脑在同一个wifi环境下
运行项目:npm run serve
查找到项目所在电脑的ip地址:
App running at:
- Local: http://localhost:9999/
- Network: http://192.168.4.162:9999/ //手机上可以访问的
手机端开发:如何做适配
前端主流适配方案:rem【推荐】,vw,vh,flex布局,百分比,媒体查询
rem适配方案:
1.https://github.com/amfe/lib-flexible
2.通过rem.js文件来动态设置
全局样式
reset.css
官方地址:https://meyerweb.com/eric/tools/css/reset/
工程目录?
https://lq782655835.github.io/blogs/team-standard/recommend-vue-project-structure.html
sass在window上的安装问题
等待安装。。。
解决方案:
npm install -g --production windows-build-tools
然后再创建vue项目
vue create 项目名
Eslint 代码检验--公司肯定是开启
Eslint官网:https://eslint.org/
Eslint中文文档:https://cn.eslint.org/
Eslint报错举例:
/Users/haojinli/Desktop/testvuepro/src/main.js
6:1 error Expected space or tab after '//' in comment spaced-comment
9:1 error Expected space or tab after '//' in comment spaced-comment
10:1 error Expected space or tab after '//' in comment spaced-comment
✖ 3 problems (3 errors, 0 warnings)
3 errors and 0 warnings potentially fixable with the `--fix` option.
-
手动解决错误
报错信息中包括:报错的文件路径,报错文件的行号/列号,报错信息,报错规则
-
通过vs code解析错误:通过在vs code安装Es-lint插件来修复格式错误
-
通过Eslint --fix来修复
-
通过配置规则解决错误
数据接口:
token= Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rlc3QuMzY1bXNtay5jb20vYXBpL2FwcC9sb2dpbiIsImlhdCI6MTU4OTc4ODQyNywiZXhwIjoxNTkwMDkwODI3LCJuYmYiOjE1ODk3ODg0MjcsImp0aSI6IndWT3czcjE2VjBzMklBZEsiLCJzdWIiOjMwOSwicHJ2IjoiOWYxZmU5ZTBkZmZiZTQ0NDJkYzc4MzEwNzUxZjU5MWNmNGQxNDAyMCJ9.wSP5E09n3TfcdrlPrrbAzPOsIHhI5lpNIBeNmjgxxVI
获取手机验证码接口:https://test.365msmk.com/api/app/smsCode
请求方式:post
请求参数:
参数名 | 参数含义 | 参数值 |
---|---|---|
mobile | 要填入的手机号 | 11手机号 |
sms_type | 登录类型 | 验证码登录 login |
登录接口:https://test.365msmk.com/api/app/login
请求方式:post
请求参数:
参数名 | 参数含义 | 参数值 |
---|---|---|
mobile | 要填入的手机号 | 11手机号 |
sms_code | 要输入的验证码 | 手机的验证码 |
type | 登录类型 | 2为验证登录登录 |
svg
icon图标:
img,雪碧图,base64(将图片转换成base64),
字体图标,svg:可以灵活配置
svg:
单个svg图:
1. 通过img引入svg
2. 通过直接插入svg
例如:
<svg t="1589851025817" class="icon search" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2154" width="200" height="200"><path d="M908.6 883.1l-155-155c28.1-30.9 50.5-66 66.8-104.4 19.3-45.6 29-94 29-143.9 0-49.9-9.8-98.3-29-143.9-18.6-44-45.3-83.5-79.2-117.5-33.9-33.9-73.5-60.6-117.5-79.2-45.6-19.3-94-29-143.9-29-49.9 0-98.3 9.8-143.9 29-44 18.6-83.5 45.3-117.5 79.2s-60.6 73.5-79.2 117.5c-19.3 45.6-29 94-29 143.9 0 49.9 9.8 98.3 29 143.9 18.6 44 45.3 83.5 79.2 117.5s73.5 60.6 117.5 79.2c45.6 19.3 94 29 143.9 29 49.9 0 98.3-9.8 143.9-29 38.5-16.3 73.6-38.7 104.4-66.8l155 155c3.5 3.5 8.1 5.3 12.7 5.3s9.2-1.8 12.7-5.3c7.1-7.1 7.1-18.5 0.1-25.5z m-428.8-69.7c-184 0-333.6-149.7-333.6-333.6 0-184 149.7-333.6 333.6-333.6 184 0 333.6 149.7 333.6 333.6 0 184-149.6 333.6-333.6 333.6z" p-id="2155"></path></svg>
通过样式来调整,例如:
.search {
width:50px;
fill:#f00;
}
多个svg图合成:svg sprites
第一步:从iconfont挑选图标加入购物并下载
第二步:解压并打开xxx.html,找到symbol复制其中的svg代码
第三步:粘贴到vue项目public中的index.html中
第四步:使用svg
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-挑选的图标名"></use>
</svg>
一、vue路由主要用于实现单页应用(SPA)
二、vue路由模式:history,hash(默认)
三、路由懒加载(让组件按需加载)
例如:
const Home = () => import('组件路径');
const Shopping = () => import('../views/shopping.vue');
const OrderList = () => import('../views/orderlist.vue');
const My = () => import('../views/my.vue');
四、路由的高亮显示
默认类名:
.router-link-active { //高亮样式 }
自定义高亮类名:
需要在实例化路由中添加:
const router = new VueRouter({
routes,
linkActiveClass: 'active' //添加自定义类名
});
axios封装:基本不用封装,只是对请求的接口地址,超时,报错处理
安装axios
npm i axios -S
axios官方github仓库地址:https://github.com/axios/axios
- 简洁语法:
//get语法语法
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
//post请求语法
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
//可以用axios同时请求多个接口,类似于Promise.all()
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
- axios通过配置对象来请求数据
//post请求
axios({
url:'接口地址',
baseURL:‘接口默认前缀地址’
method:'post',
// data:{ }, //传参,注意:post请求有可能会用qs库去转换
data:qs.stringify(obj)
headers:{'content-tpye':'application/x-www-form-urlencoded',......}, //请求头配置
});
例如:
import qs from 'qs';
const data = { 'bar': 123 };
const options = {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: qs.stringify(data),
url,
};
axios(options);
//get请求
axios({
url:'接口地址',
method:'get',
params:{ }, //传参
headers:{'content-tpye':'x-www-form-urlencoded',......}, //请求头配置
});
- axios的默认配置
axios.defaults.baseURL = '默认接口域名url';
axios.defaults.headers['x-token'] = token值; //添加token
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
-
创建axios实例
const instance = axios.create({ baseURL: '默认接口域名url', timeout:2500 }); //instance.defaults.timeout = 2500; //给实例添加默认超时时间
5.axios拦截器
// 添加请求拦截
axios.interceptors.request.use(function (config) {
//发送成功请求
config.headers['x-token']=getToken()
return config;
}, function (error) {
//发送错误请求的拦截
return Promise.reject(error);
});
// 添加响应拦截
axios.interceptors.response.use(function (res) {
//成功返回
if(res.code===2000) {
}
if(res.code===5000) {
Tosst({
title:'登录超时'
})
router.push({
path:'/login'
})
}
return res;
}, function (error) {
//失败返回
return Promise.reject(error);
});
业务接口的封装:类似于咱们对微信小程序的接口封装思路
环境地址切换:
一、环境切换简介:
听过录音,你肯定听说过你的项目有多个接口,开发时,如何进行接口地址的切换或管理?
接口开发环境:
1.本地环境:
npm run serve
自动走测试接口:https://test.365msmk.com/
2.测试环境
https://test.365msmk.com/
3.灰度环境:只给部分用户测试使用(内测),若有问题,提出bug,再调整,直到用过一时间功能稳定下来
4.预发布环境:
4.生产环境
当我npm run build时,自动走
https://www.365msmk.com/
二、环境接口地址切换的解决方案:
-
vue官方脚手架解决方案:
参考文档:https://cli.vuejs.org/zh/guide/mode-and-env.html
在项目的根目录下,创建.env.模式名来配置,例如:
.env.development
.env.production
在文件中添加
VUE_APP_自定义名称 //环境变量1
NODE_ENV=环境名称 //环境变量2
BASE_URL=URL
注:一个模式可以包含多个环境变量
执行时通过在package.json的scripts属性中添加--mode 环境名来读取.env.模式文件中对应的环境变量
-
主要通过cross-env配合webpack来进行配置
结合node.js中的process模块读取环境变量
process.env.环境变量名
具体步骤:
- 安装cross-env包实现跨平台环境变量配置
npm install cross-env -D
- 在package.json中添加cross-env运行环境脚本
"scripts": {
"serve": "cross-env BUILD_ENV=dev vue-cli-service serve",
"build": "cross-env BUILD_ENV=prod vue-cli-service build",
}
注意:cross-env key=value 只是添加环境变量,不运行任何内容
- 给process.env添加自定义的环境变量,通过vue.config.js来添加自定义环境变量,配置如下:
module.exports = {
lintOnSave: false, // 是否进行对代码用ESlint检验,false代表不检验
devServer: {
port: 9999
},
chainWebpack: config => {
// console.log('config:::', config.plugin('define'))
config
.plugin('define')
.tap(args => {
args[0]['process.env'].BUILD_ENV = JSON.stringify(process.env.BUILD_ENV)
return args;
})
}
}
注意:BUILD_ENV=dev 名子要和build中的文件名一致,方便读取不同环境的文件
- 在src下创建build目录添加不同环境变化匹配的文件
- build
- dev.js
- prod.js
例如:在prod.js中添加配置url,例如:
module.exports = {
BASE_URL: 'https://www.365msmk.com'
}
-
在http请求的js中针对npm run 来自动读取不同环境变量
const config_env = require(`../build/${process.env.BUILD_ENV}.js`); const service = Axios.create({ baseURL: config_env.BASE_URL, timeout: 3000 })
-
适配:
rem.js,
flexible.js
.banner { width:150px===>xxrem或xxxvw }
解决适配大杀器:可以直接用px写布局,最终生成rem或vw,并且不用除以2(不用换算),从而解放生产力,提高开发 效率
npm install @moohng/postcss-px2vw --save-dev
配置:在项目根目录下创建postcss:postcss.config.js文件,并添加:
module.exports = {
plugins: {
'@moohng/postcss-px2vw': {
rootValue: 200 //参考html字号
}
}
}
vw:屏幕宽度 100vw:屏幕的百分之100
vh:屏幕高度
vmax:
vmin:
国内chrome插件下载地址:https://crxdl.com/ (不用翻墙)
-
vuex是什么:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态(面试中:vuex是vue中集中管理数据工具)
-
vue组件通讯:父子通讯(父子,子父),兄弟通讯,隔代组件通讯(A->B->C->D)
-
vuex可以实现兄弟通讯,隔代通讯,父子通讯
-
vuex通常存放哪些数据:组件之间经常共享的数据
-
vuex通常用于什么样项目:根据项目复杂程度,组件比较多,数据传递比较复杂,数据难于管理时,就会想到vuex,例如:电商,后台管理的项目
-
vuex的核心:
-
state:数据仓库
- 组件中读取state数据
第一种方式:this.$store.state.count 第二种方式:通过mapSate读取(也称做vuex辅助函数) 步骤: 1.import { mapState } from 'vuex' 2.通过computed中注入mateSate computed: { ...mapState(['count', 'token', 'index']) }
-
actions:通常用于异步方法操作,但不会在这步改变state值
- 组件中调用actions方法
第一种方法:this.$store.dispath('actions的方法名',要传递的值) 第二种方法:通过mapActions读取(也称做vuex辅助函数) 步骤: 1.import { mapActions } from 'vuex' 2.通过methods中注入mateActions methods: { ...mapActions(['count', 'token', 'index']) }
-
mutations
在actions中方法中通过commit('mutaions方法名',要传递的值);
注意:通常在muations中定义的方法名,并且mutions中代码都是同步操作
组件改变vuex中数据状态的流程:
dispath commit 逻辑操作 vue组件---------->actions--------->mutations------->改变state---->最终同步到vue组件视图上
vuex中数据持久化的问题??
实现原理:利用本地存储
localStorage,sessionStorage,cookie localStorage.setItem('cartData',JSON.stringify(obj)) //存储数据 let cartData=localStorage.getItem('cartData') //读取本地存储数据 cartData=JSON.parse(cartData); localStorage.removeItem('count') //清除某一属性指定的值 localStorage.clear() //清除全部
这里除了通过本地存储之外,也可以通过一个vuex数据持久化插件来解决
例如:vuex-persistedstate
使用方法:
第一步:安装: npm install --save vuex-persistedstate 第二步:在vuex中的index.js引入 import createPersistedState from "vuex-persistedstate"; 第三步:在vuex实例上通过plugins注入 export default new Vuex.Store({ ... plugins: [createPersistedState()] })
- getters: getters相当于是vuex中的state的计算属性
第一种方法:this.$store.getters.getters的计算属性名 第二种方法:通过mapGetters读取(也称做vuex辅助函数) 步骤: 1.import { mapGetters } from 'vuex' 2.通过computed中注入mateActions computed: { ...mapGetters(['count', 'token', 'index']) }
- module:为了解决应用的所有状态会集中到一个比较大的对象的臃肿,管理复杂的问题
读取module中的数据状态 在compute中通过this.$store.state.module的模块属性.模块内部的属性名 例如: export default new Vuex.Store({ ... modules: { user: user, orderlist: orderlist } }) 注:无论用module切割多少个store,最终都会将state,actions,modules合并到一个store树上面
- 插件:plugins 为了扩展vuex的功能,例如:vuex的数据持久化插件
-
-
vuex的工程目录
- src └── store ├── index.js # 我们组装模块并导出 store 的地方 ├── actions.js # 根级别的 action ├── mutations.js # 根级别的 mutation └── modules ├── cart.js # 购物车模块 └── products.js # 产品模块
-
mutations-type 常量化
将mutations的公共常量单独存放到一个文件中,便于mutations方法名的管理 例如: export const INCREMENT = 'INCREMENT' export const DECREMENT = 'DECREMENT' 然后再mutation.js中引入,如下: import { INCREMENT, DECREMENT } from './mutation-types'
-
js文件的自动导入【不是必须会,但会了会让生活更美好--】
webpack中有一个require.context可实现文件的自动导入
//读取./modules/目录下的所有js文件
const moduleFiles = require.context('./modules/', true, /\.js$/);
//console.log('moduleFiles:',moduleFiles.keys())
//通过recude遍历获取所有store,组成{ 文件名:{actions,getters,mutations}: }
const modules = moduleFiles.keys().reduce((module, modulePath) => {
//获取store名
const moduleName = modulePath.replace(/\.\/(.+)\.\w+/, '$1')
//取出当前store所有内容
const value = moduleFiles(modulePath).default
module[moduleName] = value;
return module;
}, {})
数组reduce用法mdn参考文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
正则你还不明白吗,看文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp
如何取出文件名的主名部分: ./orderlist.js 我想取出orderlist如何取出
核心思路:通过正则表达取出
一、你对组件化的理解?组件树
参考:https://cn.vuejs.org/v2/guide/components.html
二、 vue组件通讯:组件之间数据是怎么传递的
-
父子通讯:
父->子:props
在子组件中通过props接收: props:[]或{} 通常接收时通过 { } 来接收父组件传过来的数据 例如: props: { title: { type: String, //检查接收的数据类型 default: '请输入标题', //设置如果没有传递数据时的默认值 required:true //必填项,不传递会报错提醒 } } 官方建议:不要直接将props接收的值直接渲染到页面,可以通过将接收的值传递给data或computed处理后再展现到页面上 例如: props: { title: { type: String, default: '请输入标题', required: true } }, data () { return { t: this.title } }, computed: { biaoti() { return `处理过的标题${this.title}` } } 注意:不能直接修改props接收过来的值 能否通过改变接收的props值,来更新父组件对应的值? props单向数据流: 父---》子 子---》不能改变父组件的值 通过添加sync属性配合$emit实现父子通讯的双向数据流: 父组件: <header-com :title.sync="title" /> 子组件: this.$emit('update:title', '1906A')
子->父:$emit 通过事件派发机制
this.$emit('要派发的自定义事件名',要传递的值)
例如:
this.$emit('confirm', { name: this.name, price: this.price })
$parent:子组件直接父组件
$children:父组件直接调用子组件
ref:通过ref给dom元素或组件本身添加名称来实现
例如: <mask-dialog ref="dialog" /> JS中通过this.$refs.名子访问 例如:this.$refs.dialog
思考一下:
如何在父组件中直接调用子组件的方法? this.$children
home.vue父组件
Dialog.vue子组件
如何在子组件中直接调用父组件的方法? this.$parent.方法()
-
兄弟通讯(非父子)
bus方式,除了用于兄弟通讯外,也可以用于隔代,跨代通讯
如下图:
bus通讯:
- 创建一个空的vue并导出
import Vue from 'vue' const Bus = new Vue(); export default Bus;
- 在main.js中注入bus
//引入bus import Bus from './utils/bus' // 挂载到Vue原型上 Vue.prototype.$bus = Bus;
- 实现派发和监听
A组件派发 this.$bus.$emit('up', this.str) B组件监听: created () { this.getData(); }, methods: { getData () { // 监听派发的事件 this.$bus.$on('up', v => { console.log(v) v && (this.str = v); }) } }
用bus实现跨代通讯:例如:实现侧滑栏
vuex
vue
-
隔代通讯
bus
vuex
provide和inject:一般平时用不少,除非你要开发vue组件库时才会用上
provide () { return { msg: 'hello,vuejs', app: this } } 或 provide:{ msg: 'hello,vuejs', app: this
} ```
$attrs
主要用于接收没有通过props传递的属性,可以通过$attrs将父组件(shopping.vue)的值传递给中间组件(A组件)的子组件(B组件)
```
例如: <Bcom v-bind="$attrs"></Bcom>
B组件内部接收:通过{{ $attr.属性名接收}}
例如:{{ $attrs.name }}
```
购物车组件>A组件->B组件
$listeners:主要用于由子组件向父组件传递事件
```
例如:由B组件派发事件到购物车组件,购物车如果要监听的事件,必须给A组件内部的B组件通过v-on添加$listeners,
<Bcom v-bind="$attrs" v-on="$listeners"></Bcom>
```
> **说的再俗气一点:用本地存储也可以实现上面的所有通讯方式**
```
组件通讯详解参考资料:https://www.cnblogs.com/majj/p/12617802.html ```
三、你怎么封装组件【重点】
封装组件三要素:
1.props:父传子
2.事件(派发和监听)
this.$emit()派发事件
通过@或$on来监听事件
3.slot(插槽),内容分发
命名插槽
<slot name="命名插槽名"></slot>
例如:<slot name="qrcode"></slot>
使用:
<div slot="qrcode">
.....
</div>
封装组件思考:
1.你想向用户暴露哪些属性 (props)
2.你想向用户暴露哪些事件,让用户监听来处理后续的业务 ($emit,@)
3.你想让用户嵌入哪些自定义的内容(slot)
四、组件的mixins(也称混入)
即将多个组件中,逻辑相同的部分抽离出来,相当于组件之间相同逻辑的复用!
步骤:
第一步:创建一个mixin文件(js文件)
const mixins = {
data() {
return {
w:'word'
}
},
methods: {
// 确定
confirm () {
// this.app.getData(666);
// 子级派发confirm事件
this.$emit('confirm', { name: this.name, price: this.price })
},
// 取消
cancel () {
this.$emit('cancel')
}
}
}
export default mixins;
第二步:引入到组件中并且通过mixins注入
import mixins from '../utils/mixin
export default {
name: 'maskDialog',
mixins: [mixins],
props: {},
methods:{}
}
五、组件分类:
1.业务组件 只能在当前项目中使用的组件例如:购物车,商品列表,登录 职位:前端程序员,码农,coder
2.公共组件:在项目中可以通用的项目 例如:星星评分,弹框, 职位:前端负责人,组长
3.基础组件:任何项目都能使用的组件 例如:封装一个icon图标组件,button按钮 职位:架构师,
作业:提升造轮子能力
-
封装一个星星评分组件
参考资源:
https://www.cnblogs.com/conglvse/p/9562521.html https://www.cnblogs.com/chun321/p/9456663.html
2. 封装一个返回顶部的组件
3. 封装一个搜索组件
4.封装一个select下拉组件
```
https://www.cnblogs.com/pengfei-nie/p/9134367.html ```
5.封装一个弹框组件
6.封装一个switch组件
7.封装Toast组件
.......
<button @click.native="方法名"></button>
一个组件的根元素上直接监听一个JS原生事件
Vue.js 开发使用“数据驱动”的方式思考,避免直接接触 DOM
为了在数据变化之后等待完成DOM更新后,再操作DOM,这时就要用到this.$nextTick
注意:数据更新和DOM更新不是同步的,是异步的
例如:改变dom样式,针对第三方插件(就是针对dom,swiper,滚动插件)的集成
回答:nextTick主要用于获取dom更新后的操作,只是数据操作,不涉及DOM操作,不用使用nextTick
通过JS渲染函数写法转换成了template和JSX的写法
渲染函数:就是通过JS来实现模板渲染,最终也是编译成虚拟DOM,但要求JS功底比较好
render:h=>{ 写JS实现模板渲染 }
JSX:类似于vue的template,最终也是编译成虚拟DOM
<div class='active'></div> 编译成虚拟DOM: { tag:'div' attrs:{clas:'active'} }
<label for='username'></label>
绑定事件:on事件名 例如:onClick 驼峰写法
类名:className='active'
<label htmlFor='username'></label>
template 或 render写法(用户体验好的推荐用JSX)
Vue过滤器: 怎么理解:将后台返回的数据换一种形式输出,不改变原来的数据 应用场景:后台返回的状态码(性别,支付状态),商品价格
面试时:最后你再说如何使用,例如:有全局的,局部过虑器
- 全局过滤器
Vue.filter('过滤器',对应的过滤器函数)
例如:
Vue.filer('currency' (v, type) {
console.log('返回值:', v)
const result = v && v.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
return type + result;
})
/**
* 支付状态处理
*/
Vue.filter('pay' (v) {
let payText = '';
switch (v) {
case 0: payText = '已支付'; break;
case 1: payText = '未支付'; break;
default: payText = '未知状态'; break;
}
return payText
})
最后可以通过单独创建过滤器文件并导出过滤器函数,最后通过Object.keys来集中处理
例如:
过滤器文件:filters.js
/**
* 货币格式
*/
export function currency (v, type) {
console.log('返回值:', v)
const result = v && v.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
return type + result;
}
/**
* 支付状态处理
*/
export function pay (v) {
let payText = '';
switch (v) {
case 0: payText = '已支付'; break;
case 1: payText = '未支付'; break;
default: payText = '未知状态'; break;
}
return payText
}
然后再main.js中导入并转换成过滤器函数
//引入过滤器函数
import * as filters from './filters'
//转换成过滤器函数
Object.keys(filters).forEach(key => {
Vue.filter(key,filters[key])
})
- 局部过滤器
通过在Vue实例上挂载filers添加过滤器,只能在当前组件内部使用
自定义指令
Vue指令分类:
-
内置指令:
v-model v-for v-if v-on 简写:@ v-bind 简写:: v-show v-cloak:解决网速加载慢,出现{{}} 号的问题 [v-cloak] { display: none; } <div v-cloak> {{ message }} </div>
-
自定义指令:
主要用于对DOM操作的封装,方便在组件中复用
Vue中有DOM操作相关的:
ref:主要用于当前页面对DOM的操作
自定义指令:封装成自定义指令,可以多个页面或组件中使用,复用性强
原生DOM有哪些方法
获取id:document.getElementById('')
获取类名:getElementsByClassName()
获取标签名:getElementsByTagName()
创建dom: createElment('span')
删除DOM:removeChild()
打开输入框获取到焦点?
focus()
实现拖拽:<div v-drag></div>
实现拖拽的原理:
三大事件:mousedown(鼠标按下),mousemove(鼠标移动),mouseup(鼠标抬起)
通过获取鼠标位置来动态改变dom样式
Vue自定义可复用的方案:
-
自定义组件(.vue)
-
自定义过滤器:转换和处理数据格式
-
自定义指令:用于封装DOM的,方便复用
-
mixins:用于抽离组件间相同逻辑
-
插件功能: 例如:vue-router,vuex,vue数据持久化插件
可以发布到npm上 npm install 安装插件 本地使用 例如: npm install vuex import Vuex from 'vuex' Vue.use(Vuex)
任务:用自定义指令实现一个手机端拖拽
组件上使用v-model
面试时可能会问:
组件上如何使用v-model
或不用v-model如何实现数据的双向绑定
v-model本质上就是一个语法糖,其实就是由value属性和input事件来实现
为了让它正常工作,这个组件内的 <input> 必须:
将其 value attribute 绑定到一个名叫 value 的 prop 上
在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出
一、递归:函数自己调用自己
//递归可以将复杂的问题简化
//递归特征:必须有一个能结束递归的条件
例如:
1.阶乘
function jiecheng(n) {
if(n===1) return 1;
return n * jiecheng(n-1)
}
2.fibonaci数列
function fibonaci(n) {
if(n<=2) return 1;
return fibonaci(n-1)+fibonaci(n-2);
}
二、vue递归组件
-
不用递归实现:循环遍历
+0电子产品
+1电视
2 philips
TCL
海信
+1电脑
Mac Air
Mac Pro
ThinkPad
可穿戴设置
-
用vue递归组件实现:组件自身调用自身
组件自己调用自己的name名来实现递归调用 添加一个显示和隐藏的功能 ```
具体代码如下:
父组件(my.vue)代码:
<template>
<div>
<tree-com
:title="treeObj.name"
:list="treeObj.children"
:depth="0">
</tree-com>
</div>
</template>
<script>
import TreeCom from '../components/tree'
// 模拟一个树形结构【注意:应该是后台返回的数据】
const treeObj = {
name: '电子产品',
children: [
{
name: '电视',
children: [
{
name: 'philips',
children: [
{ name: 'philips-A' },
{ name: 'philips-B' }
]
},
{ name: 'Tcl' },
{ name: '海信' }
]
},
{
name: '电脑',
children: [
{ name: 'Mac Air' },
{ name: 'Mac Pro' },
{
name: 'ThinkPad',
children: [
{
name: 'ThinkPad-A',
children:[
{name:'ThinkPad-A-A'},
{name:'ThinkPad-A-B'},
{name:'ThinkPad-A-C'},
] },
{ name: 'ThinkPad-B' }
]
}
]
},
{
name: '可穿戴设置',
children: [
{
name: '手表',
children: [
{ name: 'iWatch' },
{ name: '小米watch' }
]
}
]
}
]
}
export default {
name: 'my',
data () {
return {
treeObj
}
},
components: { TreeCom }
}
</script>
<style lang="scss">
</style>
树形组件(tree.vue)代码:
<template>
<div>
<div class="title" :style="indent" @click="toggle">
<span>{{ isShow ? '-':'+' }}</span>
{{ title }}
</div>
<div v-if="isShow">
<tree-com
v-for="(item,index) in list"
:key="index"
:title="item.name"
:list="item.children"
:depth="depth+1" //树形样式缩进
>
</tree-com>
</div>
</div>
</template>
<script>
export default {
name: 'tree-com', // 通过组件自身的name来实现组件的递归调用
data () {
return {
isShow: true, //显示或隐藏状态控制
}
},
props: {
title: { //当前接收要显示的标题
type: String,
default: '名称'
},
list: { //要递归的数组
type: Array
},
depth: { //显示层级
type: Number,
default: 0
}
},
computed: {
//通过计算属性计算缩进样式
indent () {
return `transform:translate(${this.depth * 20}px)`
}
},
methods: {
//切换显示隐藏状态
toggle () {
this.isShow = !this.isShow;
}
}
}
</script>
<style lang="scss">
</style>
一、 从面试角度,可能会问以下几个问题:
-
前端路由实现原理:
浏览器哈希值:在浏览器url地址后面添加#/名子,如果改变哈希值,通过onhashchange可以监听到到变化 参考资料:https://blog.csdn.net/sunxinty/article/details/52586556 通过浏览器H5暴露的History API 参考mdn的history官方文档:https://developer.mozilla.org/zh-CN/docs/Web/API/History 参考博客:https://www.zhangxinxu.com/wordpress/2013/06/html5-history-api-pushstate-replacestate-ajax/
-
Vue路由模式有哪几个:
通过mode来设置Vue路由模式:
hash:,带# 【默认】
优点:兼容性好,不需要后端配置 缺点:路径不美观 例如:http://127.0.0.1:9999/#/shopping
history:通过/来分隔路径
```
优点:路径美观 http://127.0.0.1:9999/shopping 缺点:有兼容性和需要后端配置,如果后端不配置路径重定义,点击页面会报打不开的错误 ```
-
Vue路由懒加载
例如:const Home = () => import('../views/home.vue');
-
Vue路由如何传参,动态路由
query传参: 1.视图传递: <router-link to="/路径?名称=会传递的值">内容</router-link> 或 <router-link :to="{path:'/user',query:{ name:'1906A' }}">内容</router-link> 注意:不用配置路由配: 3.视图接收: <div>User {{ $route.query.name }}</div>' 在JS中接收: this.$route.pquery.name params传参 1.视图传递: <router-link to="/路径/会传递的值">内容</router-link> 例如: <router-link to="/user/1906A">/user/bar</router-link> 2.路由配置: const router = new VueRouter({ routes: [ { path: '/user/:name', component: User } ] }) 3.视图接收: <div>User {{ $route.params.id }}</div>' 在JS中接收: this.$route.params.id 参考官方文档:https://router.vuejs.org/zh/guide/essentials/navigation.html
-
路由守卫(或称为路由钩子函数)
参考官方:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%89%8D%E7%BD%AE%E5%AE%88%E5%8D%AB
-
历史回退:
router.go(-1) //返回上一页 this.$route和this.$router的区别是什么 this.$route:获取路由参数 this.$router:跳转页面
-
路由404页
{ // 匹配不符合上面路径的路由
path: '/*',
component: NotFound
}
- 响应路由参数的变化
动态路由传参:如果给一个路由组件传递不同参数,如何响应参数的变化,主要通过watch来监听$route
watch:{
$route(to,from) {
console.log('watch:',to.params.type)
}
},
-
嵌套路由:可以实现二级路由,三级路由。。。。。
通过配置路由的children和要在哪个页面展示对应路由的router-view配合实现
例如: { path: '/my', component: My, children: [ { //配置默认的二级路由 path: '', component: My1 }, { path: 'my2', component: My2 }, { path: 'my3', component: My3 } ] },
-
命名视图
可以在一个组件中展现多个组件视图
{
path: '/my',
component: My,
//嵌套路由
children: [
{
path: '',
components: {
//在My组件中添加2个视图页面
my_banlance: My2,
my_order: My3
}
}
-
命名路由
{ path: '/my', name: 'my', //通过给路由命名 component: My, }