1.计算属性
关键字computed,主要用于处理一些复杂逻辑。
<div id="app">
{{5+5}}<br>
{{ ok ? 'YES' : 'NO' }}<br>
{{ message.split('').reverse().join('') }} // 反转字符串
</div>
以上例子,反转字符串逻辑稍微复杂,其余模板语法即可解决。
1.默认getter方法
以反转字符串为例,学习计算属性:
<div id="app">
<p>原始字符串: {{ message }}</p>
<p>计算后反转字符串: {{ reversedMessage }}</p>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Runoob!'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
</script>
解释:声明了一个计算属性reversedMessage,这个函数将用于vm.reversedMessage 的 getter 。vm.reversedMessage 依赖于 vm.message,在 vm.message 发生改变时,vm.reversedMessage 也会更新。
2.computed vs methods
两者效果一样,区别在于:值不变时,computed不执行,methods每次都会被调用,computed的性能比methods好。
setter
计算属性默认getter方法,只使用返回值;如果需要改变data对象中的值时,需要用到setter方法。
var vm = new Vue({
el: '#app',
data: {
name: 'Google',
url: 'http://www.google.com'
},
computed: {
site: {
// getter
get: function () {
return this.name + ' ' + this.url
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.name = names[0]
this.url = names[names.length - 1]
}
}
}
})
// 调用 setter, vm.name 和 vm.url 也会被对应更新
vm.site = '菜鸟教程 http://www.runoob.com';
document.write('name: ' + vm.name);
document.write('<br>');
document.write('url: ' + vm.url);
2.监听属性
1.监听属性
<div id = "computed_props">
千米 : <input type = "text" v-model = "kilometers">
米 : <input type = "text" v-model = "meters">
</div>
<p id="info"></p>
<script type = "text/javascript">
var vm = new Vue({
el: '#computed_props',
data: {
kilometers : 0,
meters:0
},
methods: {
},
computed :{
},
watch : {
kilometers:function(val) {
this.kilometers = val;
this.meters = this.kilometers * 1000
},
meters : function (val) {
this.kilometers = val/ 1000;
this.meters = val;
}
}
});
// $watch 是一个实例方法
vm.$watch('kilometers', function (newValue, oldValue) {
// 这个回调将在 vm.kilometers 改变后调用
document.getElementById ("info").innerHTML = "修改前值为: " + oldValue + ",修改后值为: " + newValue;
})
</script>
2. computed 和 watch 的区别
| 特性 | computed | watch |
|---|---|---|
| 缓存 | 有缓存,依赖不变时不会重新计算 | 无缓存,每次变化都会触发回调 |
| 返回值 | 必须返回一个值 | 不需要返回值 |
| 使用场景 | 适合计算新值 | 适合执行副作用(如异步操作) |
| 监听多个数据 | 可以依赖多个数据 | 只能监听单个数据 |
| 性能 | 高效,适合频繁计算 | 适合低频或复杂操作 |
总结
- 使用
computed:当你需要根据现有数据动态计算一个新值,且这个计算逻辑较为简单或需要缓存时。 - 使用
watch:当你需要在数据变化时执行异步操作、复杂逻辑或副作用时。
根据具体需求选择合适的工具,可以让代码更加清晰和高效!
3.表单上的双向绑定
v-model 指令在表单控件元素上创建双向数据绑定。
v-model 会根据控件类型自动选取正确的方法来更新元素。 一般用在输入框、单选框、复选框、选择框中。
修饰符
.lazy
在默认情况下, v-model 在 input 事件中同步输入框的值与数据,但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步:
<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model.lazy="msg" >
.number
如果想自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),可以添加一个修饰符 number 给 v-model 来处理输入值:
<input v-model.number="age" type="number">
这通常很有用,因为在 type="number" 时 HTML 中输入的值也总是会返回字符串类型。
.trim
如果要自动过滤用户输入的首尾空格,可以添加 trim 修饰符到 v-model 上过滤输入:
<input v-model.trim="msg">
4.Vue.js组件
1.父传子
子组件接收父组件的数据使用prop属性。子组件显式使用props声明。
注意: prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。
2.子传父
父组件定义数据改变的方法,使用 v-on 绑定自定义事件;子组件 使用 $emit(eventName) 触发事件。
this.$emit('increment', args) // 方法名 参数
3.data 必须是一个函数
data: function () {
return {
count: 0
}
}
分析下面代码:
var buttonCounter2Data = {
count: 0
}
Vue.component('button-counter2', {
data: function () {
return buttonCounter2Data
},
template: '<button v-on:click="count++">点击了 {{ count }} 次。</button>' })
在上述代码中,data 函数返回的是 buttonCounter2Data 对象。这意味着所有 button-counter2 组件实例都会引用同一个 buttonCounter2Data 对象。因此,当一个按钮被点击时,count 属性的值会增加,并且这个变化会反映在所有按钮上,因为它们共享同一个数据对象。
问题核心
代码中存在的主要问题是 data 选项的返回值设置不当。在 Vue 组件中,data 选项通常应该是一个函数,而不是一个对象。当 data 是一个对象时,所有组件实例都会共享这个对象的引用,导致一个组件实例对数据的修改会影响到其他组件实例。
正确的实现方式
为了确保每个组件实例都有自己独立的数据副本,data 选项应该返回一个新的对象。正确的代码如下:
<div id="components-demo3" class="demo">
<button-counter2></button-counter2>
<button-counter2></button-counter2>
<button-counter2></button-counter2>
</div>
<script>
Vue.component('button-counter2', {
data: function () {
// data 选项是一个函数,返回一个新的对象,组件不相互影响
return {
count: 0
}
},
template: '<button v-on:click="count++">点击了 {{ count }} 次。</button>'
})
new Vue({ el: '#components-demo3' })
</script>
在这个修正后的代码中,data 函数返回一个新的对象 { count: 0 }。这样,每个 button-counter2 组件实例都会有自己独立的 count 属性,点击一个按钮不会影响其他按钮的点击次数。
总结
在 Vue 组件中,为了避免多个组件实例之间的数据共享问题,data 选项必须是一个函数,并且该函数应该返回一个新的对象。这样可以确保每个组件实例都有自己独立的数据副本,从而实现组件之间的数据隔离。
5.路由
1.安装
cnpm install vue-router
2.实例
Vue.js + vue-router 可以很简单的实现单页应用。
<router-link>是一个组件,该组件用于设置一个导航链接,切换不同 HTML 内容。to 属性为目标地址, 即要显示的内容。
以下实例中我们将 vue-router 加进来,然后配置组件和路由映射,再告诉 vue-router 在哪里渲染它们。代码如下所示:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
router/index.js
// 0. 如果使用模块化机制编程,导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)
// 1. 定义(路由)组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写)相当于 routes: routes
})
// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
router
}).$mount('#app')
// 现在,应用已经启动了!
6.Vue.js Ajax
1.axios
Vue.js 2.0 版本推荐使用 axios 来完成 ajax 请求。Axios 是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中。
$ npm install axios
2.GET请求和POST请求
GET请求
// 直接在 URL 上添加参数 ID=12345
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 也可以通过 params 设置参数:
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
POST请求
axios.post('/user', {
firstName: 'Fred', // 参数 firstName
lastName: 'Flintstone' // 参数 lastName
})
.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.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// 两个请求现在都执行完成
}));
2.拦截器
request.js
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});