跳到主要内容

Vue与axios

· 阅读需 7 分钟
Yana Ching
Front End Engineer

异步调用

  • 异步效果
    • 定时任务
    • AJAX
    • 事件函数
  • 多次异步调用的依赖分析
    • 多次异步调用的结果顺序不确定
    • 异步调用结果如果存在依赖需要嵌套(会导致 => 回调地狱)

Promise

异步编程的解决方案,Promise 是一个对象,获取异步操作的消息

  • 避免多层异步调用嵌套问题(回调地狱)
  • Promise 对象提供了简洁的 API,使得控制异步操作更加简单

返回值: 使用 Promise 可以返回 Promise对象或者直接返回数据

  1. 如果返回 Promise 对象,则由该 Promise 对象调用下一个then
  2. 如果直接返回 普通值,则默认产生一个新的 Promise 对象去调用下一个then
<script>
// 1. 处理原生 AJAX
function queryData(url) {

return new Promise(function(resolve, reject) {
var xhr =new XMLHttpRequest()
xhr.onreadystatechange = function() {
if(xhr.readyState != 4) return
if(xhr.status === 200) {
resolve(xhr.responseText)
}else{
reject('ERROR!')
}
}
xhr.open('get', url)
xhr.send('key1=value1&value2=key2')
})

}
// queryData('http://site01.com/vue01/users.php').then(function(data) {
// console.log(data)
// })
// ==============================================
// 发送多次 AJAX 请求并且保证顺序
// 保证链式编程,避免回调地狱
// 使用 Promise 可以返回 Promise对象或者直接返回数据
// 1. 如果返回 Promise 对象,则由该 Promise 对象调用下一个then
// 2. 如果直接返回 普通值,则默认产生一个新的 Promise 对象去调用下一个then
queryData('http://site01.com/vue01/users.php?id=2').then(function(data) {
console.log(data)
return queryData('http://site01.com/vue01/time.php') // 返回 第二次请求 promise对象
}).then(function(data) {
console.log(data)
return queryData('http://site01.com/vue01/users.php?id=20') // 返回 第三次请求 promise对象
}).then(function(data) {
console.log(data)
return '第三次请求' // 返回数据,则下一个then接收该参数
}).then(function(data) {
console.log(data+"-------")
})
</script>

Promise 常用 API

实例方法

  • p.then() 得到异步任务的正确结果
  • p.catch() 得到异常信息
  • p.finally() 成功与否都会执行

静态方法

  • Promise.all( [ ] )

  • Promise.race( [ ] )

<script>
function queryData(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
if(xhr.readyState != 4) return
if(xhr.readyState == 4 && xhr.status == 200) {
resolve(xhr.responseText)
}else{
reject('服务器错误')
}
}
xhr.open('get', url)
xhr.send('key1=value1&key2=value2')
})
}
var p1 = queryData('http://site01.com/vue01/users.php?id=2')
var p2 = queryData('http://site01.com/vue01/users.php?id=4')
var p3 = queryData('http://site01.com/vue01/users.php?id=6')
Promise.all([p1, p2, p3]).then(function(result) {
console.log(result)
return Promise.race([p1, p2, p3])
})
.then(function(res) {
console.log(res)
})
</script>
<script type="text/plain">
1. 数组中对象 均为 promise 实例(如果不是,则默认使用 Promise.resolve 转为 promise 实例)
2. Promise.all([]) 接收 数组 做参数
3. Promise.race([]) 接收 数组 作参数,当数组实例对象状态改变(fulfilled或rejected)的时候,p实例的状态跟着改变,返回第一个改变状态的promise的返回值,传给 p 的回调函数
4. all() 所有任务都执行才有结果 return array
5. race() 只要有一个完成任务就有结果 return object
</script>

fetch

  • 不是 AJAX 的 进一步封装,而是原生 js,没有使用 XMLHttpRequest 对象

  • fetch( url, options ).then( )

响应格式数据

  • text( ) 将返回体处理成 字符串 形式
  • json( ) 返回结果和 JSON.parse(responseText) 一样

axios

基于Promise用于浏览器和 node.js 的 HTTP 客户端

  • 支持 浏览器 和 node.js
  • 支持 Promise
  • 能拦截请求和响应
  • 自动转换 JSON 数据
axios.get( url )
.then(ret=>{
// data属性名称是固定的,用户获取后台响应的数据
console.log(ret.data)
})

axios 常用 API

  • get: 查询数据
  • post: 添加数据
  • put: 修改数据
  • delete: 删除数据

GET传递参数

  • 通过 URL 传递参数
axios.get('/data?id=123')
.then(ret=>{
console.log(ret.data)
})
axios.get('/data/123')
.then(ret=>{
console.log(ret.data)
})
// restful格式请求
  • 通过 params 选项传递参数
axios.get('/data', {
params: {
id: 123
}
})
.then(ret=>{
console.log(ret.data)
})

DELETE传递参数

参数传递方式与 GET 类似

POST传递参数

  • 通过选项传递参数(默认 json格式的数据)
axios.post('/data', {
uname: 'tom',
pwd: 123
}).then(ret=>{
console.log(ret.data)
})
  • 通过 URLSearchParams 传递参数(application/x-www-form-urlencoded 表单格式数据 - 键值对)
var params = new URLSearchParams()
params.append('uname', 'zhangsan')
params.append('pwd', '111')
axios.post('http://site01.com/vue01/test.php/44', params)
.then(function(ret){      
console.log(ret.data)  
})

####### PUT 传递参数

axios.put('/data', {
uname: 'tom',
pwd: 123
}).then(ret=>{
console.log(ret.data)
})

axios 响应结果

  • data:实际响应回来的数据
  • headers:响应头信息
  • status:响应码信息
  • statusText:响应状态信息

axios 全局配置

- axios.defaults.timeout = 3000; //超时时间
- axios.defaults.baseURL = 'http://localhost:3000/app'; //默认地址
- axios.defaults.headers['mytoken'] = '*****************' //设置请求头

axios拦截器

请求拦截器

在请求发送前进行一些操作 例如在每个请求体里加上token,统一做了处理如果以后要改也非常容易

axios.interceptor.request.use(function(config) {
console.log(config.url)
// 任何请求都会经过这一步 在发送请求之前做些什么
config.header.mytoken = 'hello'
// 这里一定要return 否则配置不成功
return config
}, function(err){
console.log(err)
})

响应拦截器

在接收到响应后进行一些操作 例如在服务器返回登录状态失效,需要重新登录的时候,跳转到登录页

axios.interceptors.response.use(function(res) {       
// 在接收响应做些什么
var data = res.data
return data
}, function(err){
// 对响应错误做点什么
console.log(err)
})

Vue单文件组件

· 阅读需 3 分钟
Yana Ching
Front End Engineer

Vue 单文件组件的基本用法

单文件组件的组成结构

  • template 组件模板区域
  • script 业务逻辑区域
  • style 样式区域

webpack中配置vue组件的加载器

- 运行 npm i vue-loader vue-template-compiler -D 命令 
- 在 webpack.config.js 配置文件中,添加 vue-loader 的配置项如下:
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module: {
rules: [ // ... 其它规则
{ test: /\.vue$/, loader: 'vue-loader' }
]
},
plugins: [ // ... 其它插件
new VueLoaderPlugin() // 请确保引入这个插件! ]
}

运行的时候遇到错误

Module not found: Error: Can't resolve 'vue' in 'D:\Z-work space\DOC\reading_IT\webpack-study\src\components'
解决方法
npm i vue

在 webpack 项目中使用 vue

- 运行 npm i vue -S 安装vue
- 在 src -> index.js 入口文件中,通过 import Vue from 'vue' 来导入 vue 构造函数
- 创建 vue 的实例对象,并制订要控制的 el 区域
- 通过 render 函数渲染 App 根组件
// 1. 导入 Vue 构造函数 import Vue from 'vue' 
// 2. 导入 App 根组件 import App from './components/App.vue'

const vm = new Vue({
// 3. 指定 vm 实例要控制的页面区域
el: '#app',
// 4. 通过 render 函数,把指定的组件渲染到 el 区域中
render: h => h(App)
}
// render: function(createElements) {
// var html = createElements(App) // 返回渲染出来的 HTML结构
// return html
// }
// ========== 相当于 =================
// render: createElements => {
// return createElements(App)
// }
// ========== 使用时 =================
// render: h => h(App)

webpack 打包发布

通过package.json文件配置打包

"scripts" {
// 用于打包的命令
"build": "webpack -p",
// 用于开发调试的命令
"dev": "webpack-dev-server --open --host 127.0.0.1 --port 3000"
}

打包报错:

npm ERR! file D:\Z-work space\DOC\reading_IT\webpack-study\package.json npm ERR! code EJSONPARSE npm ERR! JSON.parse Failed to parse json npm ERR! JSON.parse Unexpected token / in JSON at position 223 while parsing near '...entBase src --hot", // 调试开发命令 npm ERR! JSON.parse "dev1"...' npm ERR! JSON.parse Failed to parse package.json data. npm ERR! JSON.parse package.json must be actual JSON, not just JavaScript.

原因:package.json 文件中不能添加 注释 ' // '

Vue常用特性应用场景

· 阅读需 9 分钟
Yana Ching
Front End Engineer

汇总

  • 过滤器(格式化日期)
  • 自定义指令(获取表单焦点)
  • 计算属性(统计图书数量)
  • 侦听器(验证图书存在性)
  • 生命周期(图书数据处理0

过滤器 filter - 格式化数据

提示

过滤器定义

  1. 默认第一个参数是 管道符(filter)前面的数据 在本例中指的是 message
  2. 全局过滤器 定义的时候 filter 不带s
  3. 局部过滤器与 data及methods 同级
<!-- 过滤器只能用在: 双花括号插值 + v-bind 表达式 -->
<!-- 过滤不改变真正的 data,而是改变渲染的结果 -->
<div id="box" >
<!-- {{message | filterA('10', '12')}} -->
{{message | filterB(10,20)}}
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 全局过滤器
// Vue.filter('filterA', function(n, a, b) {
// if(n<10){
// return n+a
// }else{
// return n+b
// }
// })
new Vue({
el: '#box',
data: {
message: 'haha'
},
filters: {
filterB: function(n, a, b) {
if(n < 10){
return n+a
}else{
return n+b
}
}
}
})
</script>

自定义指令

当内置指令不满足需求的时候,使用自定义指令

Vue.directive ('directiveName', {
inserted: function() {
// ....
}
})
信息
  1. 定义的时候不需要加上 'v-'
  2. 使用的时候要加上 'v-'
  3. 使用驼峰命名法 'directiveName'
  4. 自定义指令带参数(钩子函数): a. bind: 只调用一次,指令第一次绑定到元素时调用(一次性的初始化) b. inserted: 被帮顶元素插入父节点时候调用(仅保证父节点存在,但不一定被插入文档中) c. update: 所有组组件的 VNode 更新是调用,但可能发生在其子 VNode 更新之前,指令的值可能发,也可能没有 d. componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用 e. unbind: 只调用一次,指令与元素解绑时调用
  5. 局部自定义指令: 与 data及methods 同级,'directives' 意义: 当内置指令不能满足需求的时候
<!-- 当内置指令不能满足需求的时候 -->
<div id="app">
<input type="text" v-color="msg">
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 全局自定义指令 v-handle
Vue.directive('handle', {
inserted: function(el) {
el.focus()
}
})

// 局部自定义指令
new Vue({
el: '#app',
data: {
msg: {
color: 'red'
}
},
// 局部自定义指令
directives: {
color: {
bind: function(el, binding) {
el.style.backgroundColor = binding.value.color
// 默认第一个参数是指令绑定的对象,binding 形参则是传入的值
}
}
}
})
</script>

计算属性computed - 复杂逻辑计算

提示
  1. 与方法的区别: 计算属性依赖缓存,而方法不缓存
  2. 多次调用方法,每次都会重新调用计算
  3. 多次调用计算属性,只要里面的值不变,再次调用,它会把第一次计算的结果直接返回
  4. 上述例子中 仅打印一次 'computed'
<div id="app">
<div>{{msgA}}</div>
<div>{{msgA}}</div>
<div>{{msgB()}}</div>
<div>{{msgB()}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
msg: 'nnihao',
num: 40
},
methods: {
msgB: function() {
console.log('methods')
return this.msg.split('').reverse().join('')
// 将字符串分隔成 数组 之后 反转 再连接数组中所有的值 => 字符串
}
},
computed: {
msgA: function() {
console.log('computed')
return this.msg
}
}
})
</script>

侦听器- 异步执行、开销大的操作

当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的

提示
  1. 使用watch来响应数据的变化 一般用于异步或者开销较大的操作
  2. watch 中的属性 一定是data 中 已经存在的数据
  3. 当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够 监听到变化,此时就需要deep属性对对象进行深度监听
<div id="app">
<div>
<span>名:</span>
<span>
<input type="text" v-model="firstName">
</span>
</div>
<div>
<span>姓:</span>
<span>
<input type="text" v-model="lastName">
</span>
</div>
<div>
<div>{{fullName}}</div>
</div>
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: '',
lastName: ''
},
watch: {
firstName: function(val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function(val) {
this.fullName = this.firstName + ' ' + val
}
}
})
</script>

生命周期

事物从出生到死亡的过程 Vue实例从创建 到销毁的过程 ,这些过程中会伴随着一些函数的自调用。

我们称这些函数为钩子函数 常用的 钩子函数

before在实例初始化之后,数据观测和事件配置之前被调用,此时 data 和 methods 以及页面的 DOM 结构都没有初始化,什么都做不了
created在实例创建完成后被立即调用 此时 data 和 methods 已经可以使用,但是页面还没有渲染出来
beforeMount在挂载开始之前被调用,此时页面上还看不到真实数据,只是一个模板页面
mountedel 新创建的 wm.$el 替代,并挂载到实例上去之后,调用该钩子。数据已经真实选嚷道页面上,在这个钩子函数里面我们可以使用一些第三方的插件
beforeUpdate数据更新时调用,发生在虚拟 DOM 打补丁之前。页面上数据还是旧的
updated由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。页面上数据已经替换成最新的
beforeDestroy实例销毁之前调用
destroyed实力销毁后调用

数组变异方法

在 Vue 中,直接修改对象属性值无法触发响应式:数据改变,页面内容不变

变异数组方法即保持数组方法原有功能不变的前提下对其进行功能拓展

push()往数组后面添加一个元素,成功返回当前数组的长度
pop()删除数组的后一个元素,成功返回删除元素的值
shift()删除数组的第一个元素,成功返回删除元素的值
unshift()往数组前面添加一个元素,成功返回当前数组的长度
splice()有三个参数,第一个是想要删除的元素的下标(必选),第二个是想要删除的个数(必选), 第三个是删除 后想要在原位置替换的值
sort()使数组按照字符编码默认从小到大排序,成功返回排序后的数组
reverse将数组倒序,成功返回倒序后的数组

替换数组

不改变原数组,返回新数组

filte创建一个新数据,新数组中的元是经过检查指定数组中符合条件的所有元素
concat连接两个或多个数组
slice从已有数组中返回选定元素

动态数组响应式数据

  • Vue.set(a,b,c) 让 触发视图重新更新一遍,数据动态起来

  • a是要更改的数据 、 b是数据的第几项、 c是更改后的数据

Vue 组件化

· 阅读需 9 分钟
Yana Ching
Front End Engineer

组件化思想

标准 分治 重用 组合

组件化规范:Web Component

  • 尽可能重用代码
  • 自定义组件方式不太容易(HTML、css、js)
  • 多次使用组件可能导致冲突

通过封装好功能的定制元素解决上述问题

组件注册

组件命名

  • 短横线 Vue.component('my-component', { /* ... */ }) ( 推荐使用 )
  • 驼峰 Vue.component('myComponent', { /* ... */ })
    • 使用驼峰命名的组件只能在 模板 中使用驼峰命名的方式,如果要在 普通标签中使用,必须使用 短横线
// 全局注册组件
Vue.component(逐渐名称, {
data: 组件元素,
template: 组件模块内容
})
// 使用: 直接把 组件名称 当做 标签 使用即可
组件使用注意:
  1. 当模板比较复杂的时候,可以使用模板字符串 使用反引号

    Vue.component('component_name', {
    data: function(){
    return {}
    },// 要求是个函数
    template: `
    <div>
    <button></button>
    <button></button>
    </div>
    `
    // 当模板比较复杂的时候,可以使用模板字符串 使用反引号
    })
  2. data 参数必须是函数,并且要求返回一个对象

  3. 模板必须是单个根元素,即可由一个父级元素包含其他元素,不可在最外层使用多个兄弟元素

  4. 模板可重用,每个组件 data 属性中的值是相互独立的

  5. 局部注册的组件,只能在当前注册它的vue实例中使用

  6. 组件命名问题:使用 驼峰式(buttonCounter) 只能在 字符串模板 中用驼峰的方式使用组件,但是 在普通的标签中。必须使用 短横(button-counter) 的方式使用组件

var componentA = {
data: function(){
return {
msg: 'componentA'
}
}
}
var componentB = {
data: function(){
return {
msg: 'componentB'
}
}
}
var componentC = {
data: function(){
return {
msg: 'componentC'
}
}
}
new Vue({
el: '#app',
component: {
'component-a': componentA,
'component-b': componentB,
'component-c': componentC,
}
})
// 局部注册的组件,只能在当前注册它的vue实例中使用
// 全局组件 模板 中不可以使用局部注册的组件

Vue 调试工具用法

  • 克隆仓库
    • git clone https://github.com/vuejs/vue-devtools.git
  • 安装依赖包
    • cd vue-devtools
    • npm install
  • 构建
    • npm run build
  • 进入 chrome 扩展程序
    • 加载已解压的扩展程序
    • 进入已经编译好的 vue-devtools 文件夹,选择 shell>chrome

组件间数据交互

  • 父向子:子组件props接收,父组件给相应 props参数的属性 赋值

  • 子传父:在子模板中使用 $emit(函数名,参数)传值,父组件用v-on 监听子组件的事件,$event 专门用来接收传递过来的值

  • 兄弟之间:Vue实例 hub 作为事件中心,兄弟组件模板均使用钩子函数 mounted 监听 hub.$on(fnName,function),组件内设置方法,选择时机 使用 hub.$emit(fnName,传递的参数) 触发事件

子组件 通过 props 接收 传递过来的值

Vue.component('menu-item', {
props: ['title'],
template: '<div>{{title}}</div>'

})

父组件通过 属性值 将值传递给 子组件

<menu-item title="来自父组件的数据"></menu-item>    ------------ 直接写死

<menu-item :title="title"></menu-item> ------------------------------ 数据绑定
// 父传子
<div id="app">
<div>{{pmsg}}</div>
<menu-item title="写死的来自父组件的值"></menu-item>
<menu-item :title="ptitle" content="你好"></menu-item>

</div>
<script src="js/vue.js"></script>
<script>
// 父组件向子组件传值
Vue.component('menu-item', {
props: ['title','content'],
data: function() {
return {
msg: '子组件本身的数据',
}
},
template: '<div>{{msg + "======" + title + "=====" + content}}</div>'
})
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
ptitle: '父组件中动态绑定的数据'
}
})
</script>
<script type="text/plain">

父组件向子组件传值
1. Vue实例本身就是一个根组件
2. 全局定义一个子组件
3. 通过给 子组件属性 传值,子组件通过 props 以数组形式接受值 的方式 完成 =>子 的传值过程
4. props 是一个数组
</script>
// 子传父
<div id="app">
<div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
<menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>
</div>

<script src="js/vue.js"></script>
<script>
// 子组件向父组件传值 - 携带参数
Vue.component('menu-item', {
props: ['parr'],
// data: function(){
// return {

// }
// },
template: `
<div>
<ul>
<li :key='index' v-for='(item, index) in parr'>{{item}}</li>
</ul>
<button @click="$emit('enlarge-text', 5)">扩大父组件中字体大小</button>
<button @click="$emit('enlarge-text', 10)">扩大父组件中字体大小</button>
</div>
`
})
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
parr: ['apple', 'pear', 'banana'],
fontSize: 10
},
methods: {
handle: function(val){
// 扩大字体大小
this.fontSize += val
}
}
})
</script>
// 兄弟
<div id="app">
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<script src="js/vue.js"></script>
<script>
// 1. 提供事件中心
var hub = new Vue()



Vue.component('test-tom', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>TOM {{num}}</div>
<div>
<button @click="handle">点击</button>
</div>
</div>
`,
methods: {
handle: function(){
hub.$emit('jerry-event', 10)
}
},
mounted: function(){
// 模板加载完毕之后,监听事件 tom-event
hub.$on('tom-event', (val) => {
this.num += val
})
}
})

Vue.component('test-jerry', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>JERRY {{num}}</div>
<div>
<button @click="handle">点击</button>
</div>
</div>
`,
methods: {
handle: function(){
hub.$emit('tom-event', 5)
}
},
mounted: function(){
hub.$on('jerry-event', (val) => {
this.num += val
})
}
})

var vm = new Vue({
el: '#app',
data: {},
methods: {}
})
</script>
<script type="text/plain">
兄弟组件传值借助 事件中心 (Vue实例)来监听事件
1. 提供事件中心 var hub = new Vue()
2. 加载完模板之后 即 通过 mounted(){} 钩子函数(模板加载完毕) 设置监听器 hub.$on(方法名,函数)
3. 组件内设置方法,方法内部 使用 hub 事件中心 触发事件 hub.$emit(方法名, 传递的数据)
</script>

组件插槽

匿名插槽

父组件向子组件传递模板,通过子组件在自身模板中使用 slot 标签获取父组件传入的内容

子组件模板中 slot 标签填入内容的话,当父子间没有传入内容的时候,默认使用子组件内的内容

<div id="app">
<menu-item>{{msg}}</menu-item>
<menu-item></menu-item>
<menu-item>html</menu-item>

</div>
<script src="js/vue.js"></script>
<script>
Vue.component('menu-item', {
template: `
<div>
<strong>ERROR!</strong>
<slot>hello</slot>
</div>
`
})
var vm = new Vue({
el: '#app',
data: {
msg: '你好'
},
methods: {

}
})
</script>

具名插槽

<div id="app">
<base-layout>
<p slot="header"></p>
<p ></p>
<p slot="footer"></p>
<p slot="para1">段落</p>
</base-layout>

<!-- 当多个标签都要渲染到某个模板位置的时候,可以使用 template 标签做暂时包裹,该标签不会渲染到页面上的 -->
<base-layout>
<template slot="footer">
<p>footer</p>
<p>footer</p>
<p>footer</p>
<p>footer</p>
</template>
<template slot="header">
<p>header</p>
<p>header</p>
<p>header</p>
<p>header</p>
</template>
<template>
<p>000</p>
<p>000</p>
<p>000</p>
<p>000</p>
</template>
</base-layout>
</div>
<script src="js/vue.js"></script>
<script>
Vue.component('base-layout', {
template: `
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
<slot name="para1"></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
})
var vm = new Vue({
el: '#app',

})
</script>
<script type="text/plain">
1. 使用 <slot> 中的 "name" 属性绑定元素 指定当前插槽的名字
2. 具名插槽的渲染顺序,完全取决于模板,而不是取决于父组件中元素的顺序
</script>

Vue踩坑汇总

· 阅读需 2 分钟
Yana Ching
Front End Engineer

Trailing spaces not allowed

删除多余空行即可

Newline required at end of file but not found

文件末尾需要换行符,但找不到

解决:=> 在 js css等后面再加一行(空行)

error Extra semicolon semi

不使用分号

多次引入 element-ui

2:24  error  'E:\Develop\Apache\htdocs\econ.com\node_modules\element-ui\lib\element-ui.common.js' imported multiple times  import/no-duplicates
3:32 error 'E:\Develop\Apache\htdocs\econ.com\node_modules\element-ui\lib\element-ui.common.js' imported multiple times import/no-duplicates
4:23 error 'E:\Develop\Apache\htdocs\econ.com\node_modules\element-ui\lib\element-ui.common.js' imported multiple times import/no-duplicates
6:1 error More than 1 blank line not allowed no-multiple-empty-lines

解决方法:

使用一次 import 即可

// ========== before ===========
import { Button } from 'element-ui'
import { Form, FormItem } from 'element-ui'
import { Input } from 'element-ui'
// ========== before ===========
import { Button, Form, FormItem, Input } from 'element-ui'

tab四字节导致的错误

31:1 error Expected indentation of 4 spaces but found 0 indent 33:1 error Expected indentation of 6 spaces but found 4 indent 34:1 error Expected indentation of 8 spaces but found 4 indent 35:1 error Expected indentation of 8 spaces but found 4 indent 36:1 error Expected indentation of 6 spaces but found 4 indent 37:1 error Expected indentation of 4 spaces but found 0 indent 38:1 error Expected indentation of 2 spaces but found 0 indent

修改编辑器tab 键字节数,修改设置settings.json

"editor.tabSize": 4:指定一个tab等于多少个空格,例如此处指定4就像等于4个空格,2就等于两个空格
"editor.detectIndentation":false :必须指定!!否则指定的tab大小将不起效果

没必要的返回语句

Unnecessary return statement no-useless-return

login() {
// console.log(this)登陆时候的预校验。
this.$refs.loginFormRef.validate(valid => {
if (!valid) return
})
}
// ========= 修改 ============
login() {
// console.log(this)登陆时候的预校验。
this.$refs.loginFormRef.validate(valid => {
if (!valid) return false
})
}

vue ui 启动可视化面板一片空白

删除 C:\Users\Administrator 下的 '.vue-cli-ui' 文件即可

Webpack Error Debug

· 阅读需 2 分钟
Yana Ching
Front End Engineer

Module not found: Error: Can't resolve ' XX ' in ' XXXX '

::: tip 重新安装项目依赖 :::

npm i XX

[Vue warn]: Cannot find element: #app

打包好的 js(webpack中的 bundle.js) 文件要最后引入,因为要先有 #app 挂载的 div 元素,Vue 才能获取相应的元素
原来的 JS 文件位置:<head><script src="/bundle.js"></script></head>
// ================== 修正后位置 =================
<body>
<div id="app"></div>
<!-- ... -->
<script src="/bundle.js"></script>
</body>

webpack -p 打包报错

危险

npm ERR! file D:\Z-work space\DOC\reading_IT\webpack-study\package.json npm ERR! code EJSONPARSE npm ERR! JSON.parse Failed to parse json npm ERR! JSON.parse Unexpected token / in JSON at position 223 while parsing near '...entBase src --hot", // 调试开发命令 npm ERR! JSON.parse "dev1"...' npm ERR! JSON.parse Failed to parse package.json data. npm ERR! JSON.parse package.json must be actual JSON, not just JavaScript.

package.json 文件中不能添加 注释 ' // '
JSON 文件中不能添加注释 
// 很多人利用注释来制定解析规则,这破坏了互操作性(Interoperability),因此将其剔除

v-clock/text/html 使用区别

· 阅读需 3 分钟
Yana Ching
Front End Engineer

v-cloak、v-text、v-html区别

  1. 使用 v-cloak 能解决插值表达时闪烁的问题,默认 v-text 没有闪烁问题
  2. v-text 会覆盖元素中原本的内容,但是 插值表达式 只会替代自己的占位符
  3. v-html 可以输出渲染HTML标签
<div id="app">
<p v-cloak>+++++++++++ {{ msg }} +++++++++++++++++</h1>
<!-- +++++++++++ 123 +++++++++++++++++ -->

<p v-cloak=>+++++++++++ {{ msg3 }} +++++++++++++++++</h1>
<!-- +++++++++++ <del>我被删了</del> +++++++++++++++++ -->

<p v-text="msg2">我是p标签</p>
<!-- 1456 -->
<p v-text="">我是p标签</p>
<!-- 我是p标签 -->
<p v-text="msg3">我是p标签</p>
<!-- <del>我被删了</del> -->

<p v-html="msg3"></p>
<!--成功渲染出 <del></del> 标签-->
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: '123',
msg2: '1456',
msg3: '<del>我被删了</del>'
}
})
</script>

vue-cloak解决插值表达式闪烁问题

当网络加载慢时,Vue.js 还没有加载完成,如果页面上使用了插值表达式,源码就会直接在页面上显示出来,只有当 vue.js 都加载出来,才会显示插入的内容

v-claok 会保存在元素上知道关联实例编译结束,结合CSS样式:[v-cloak] {display: none;} 在内容还没有加载出来时将标签隐藏

<div id="app">
<h1 v-cloak>{{ msg }}</h1>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: '123'
}
})
</script>
注意事项:
  • @import 加载的 CSS 文件时在在 DOM 完全加载后,才会进行加载
  • 如果将[v-cloak]卸载 @import 加载的CSS文件中,就会导致页面仍然闪烁
  • 为避免这种情况,将[v-cloak]写在 link 引入的 CSS 中,或者写一个内联CSS样式既可以解决问题

Vue3.0脚手架

· 阅读需 1 分钟
Yana Ching
Front End Engineer

Vue 脚手架基本用法

快速生成 Vue 项目基础架构

- 安装 3.x 版本的 Vue 脚手架
npm install -g @vue/cli
// 或者 cnpm i @vue/cli -g
- 检查安装成功与否(脚手架版本号)
vue -V
// @vue/cli 4.2.3

基于 3.x 版本的脚手架创建 vue 项目

// 法一:交互式命令行
vue create my-project
// 法二:图形化界面
vue ui
// 基于 2.x 的旧版本创建
npm install -g @vue/cli-init
vue init webpack my-project

图形化界面

image-20200313172500044

image-20200313172710912

image-20200313172754560

image-20200313173051395

image-20200313173228996

image-20200313173331684

image-20200313173852182

Vue 脚手架生成地项目结构分析

image-20200313174425820

通过 package.json 配置项目(不推荐)

image-20200313175953548

image-20200313180147133

通过单独的配置文件配置项目 (推荐)

image-20200313180422786

Vue基础

· 阅读需 5 分钟
Yana Ching
Front End Engineer

v-bind 三种用法

  1. 直接v-bind
  2. 简化::
  3. 绑定的时候拼接绑定内容:title="btnTitle + ',这是追加内容'"

v-on 绑定事件

表单控件使用

1. radio

使用 v-model 绑定参数,当参数=value的时候,该控件被选中

2. checkbox

使用 v-model 绑定参数,当 参数=value 的时候,该控件被选中

注意:使用 checkbox 的时候,存储数据应使用 数组

获取数据:默认获取以下类型数据,如不想获得 __ob__ 监视器对象,可以使用 toString() 转成字符串

image-20200303144057272

image-20200303144357360

3. select - option

使用 v-model 绑定参数给 select 标签,对应的 多个option 标签的 value 应不同,当 参数=value时,相应的 option 被选中

该控件使用有两种情况: 1. 单选,使用数值存储即可

  1. 多选,在select标签上添加参数 multiple ,对应的参数存储使用 数组
<span>职业:</span>
<select v-model="occupation">
<option value="0">请选择职业..</option>
<option value="1">教师</option>
<option value="2">软件工程师</option>
<option value="3">动画特效师</option>
</select>

image-20200303164634858

<span>职业:</span>
<select v-model="occupation" multiple>
<option value="0">请选择职业..</option>
<option value="1">教师</option>
<option value="2">软件工程师</option>
<option value="3">动画特效师</option>
</select>

image-20200303164543760

表单修饰符

  • .number:转化为数值

    • input标签输入直接是字符串,使用 .number 就可以转化成数值,这样和数值进行运算的时候才不会变成字符串的拼接
  • .trim:去掉开始和结尾的空格

  • .lazy:将 input 时间切换为 change 事件(即在失去焦点或按下回车键才更新)

.number

<div id='app'>
<input type="text" v-model="age">
<button @click="handle">点击</button>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
age: ''
},
methods: {
handle: function () {
// console.log("age:" + this.age + '---' + this.age.length)
// console.log("month:" + this.month)
console.log(this.age + 13);
// cosole.log(this.month)
}
}
})
</script>
// 输入 14 => 1413 (字符串拼接)

image-20200304234501052

<div id='app'>
+ <input type="text" v-model.number="age">
<button @click="handle">点击</button>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
age: ''
},
methods: {
handle: function () {
// console.log("age:" + this.age + '---' + this.age.length)
// console.log("month:" + this.month)
console.log(this.age + 13);
// cosole.log(this.month)
}
}
})
</script>
// 输入 14 => 27 (数值运算)

image-20200304234404590

.trim


<div id='app'>
<label for="age">年龄</label>
<input type="text" id="age" v-model.trim="age">
<label for="month">月份</label>
<input type="text" id="month" v-model="month">
<!-- <p>{{month}}</p> -->
<!-- <input type="text" v-model="month"> -->
<button @click="handle">点击</button>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
age: '',
month: ''
},
methods: {
handle: function () {
console.log("age:" + this.age + '---' + this.age.length)
console.log("month:" + this.month + '---' + this.month.length)
}
}
})
</script>
// 年龄使用.trim,月份不使用以作对比

image-20200304235617833

image-20200304235645605

使用了 .trim 标识符的 input 在失去焦点的时候就执行该命令,马上删去开始和结尾的空格

image-20200304235841490

自定义指令

内置指令不满足需求的时候

过滤器

格式化数据,可多个使用,后一个过滤器是处理 前一个过滤器产生的结果

数组API

变异方法(修改原有数组)

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

替换数组(生成新数组)

  • filter()
  • concat()
  • slice()

数组响应式变化

通过索引修改数组不会响应式变化

  • vm.list[1] = 'lemon'

image-20200305142500790

image-20200305142807369

修改响应式数据

  • Vue.set( vm.items, indexOfItem, newValue )
  • vm.$set( vm.items, indexOfItem, newValue )
    • vm.items => 数组名称
    • indexOfItem => 数组索引 / 也可以修改对象的属性值
    • newValue => 数组的新值

image-20200305143254323

image-20200305143142020

修改对象属性值

通过属性值修改数组不会响应式变化

vm.info.gender='male'

image-20200305144555650

vm.$set(vm.info, 'gender', 'male')

image-20200305144956925

image-20200305145041888