vue.js 2.0 学习

63 阅读7分钟

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 的区别

特性computedwatch
缓存有缓存,依赖不变时不会重新计算无缓存,每次变化都会触发回调
返回值必须返回一个值不需要返回值
使用场景适合计算新值适合执行副作用(如异步操作)
监听多个数据可以依赖多个数据只能监听单个数据
性能高效,适合频繁计算适合低频或复杂操作

总结

  • 使用 computed:当你需要根据现有数据动态计算一个新值,且这个计算逻辑较为简单或需要缓存时。
  • 使用 watch:当你需要在数据变化时执行异步操作、复杂逻辑或副作用时。

根据具体需求选择合适的工具,可以让代码更加清晰和高效!

3.表单上的双向绑定

v-model 指令在表单控件元素上创建双向数据绑定。 image.png

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);
  });