关于 Vue

由于要去了解新的业务系统,还有就是要滚回去写 Android 了,这里就做一下小结吧。

感觉相对于 React 来说,Vue 使用起来更加有写前端的感觉,上手也比较快,经过一两天的摸索就大概知道要怎么写了。

相同点:

  1. 都使用了 虚拟Dom 的技术,来提高渲染的性能和效率。
  2. 都是 MVVM 的架构设计。
  3. 组件化明显
  4. 单向数据流。

不同点:

  1. React 使用的是 JSX ,Vue 使用更多是 template 。
  2. React 的门槛更高, Vue 的门槛低一些,甚至直接导入 js 文件就可以开始怼了。

练习

通过这几天的学习,也掌握了一些其他的东西,也在这里一并讲一下吧。

这个练习是仿照 豆瓣电影 的首页来做的。效果如下

跨域的问题

第一个遇到的问题是跨域,一开始使用 Vue 的时候是通过导入 js 文件来写的。网络请求用的是 ajax ,使用 jsonp 就可以轻松解决跨域的问题( jsonp 只能使用 GET 请求)

在后面学到单文件的时候,问题就不一样了。因为使用单文件,就需要使用构建工具进行预处理和构建,使用到了 webpack ,而且本地还跑起来了 node 服务,用于热更新等操作。

像 Vue 这种 MVVM 架构的框架,我就不打算使用 jq 了,也不打算使用 ajax 了。

网络请求这块使用的是 axios ,这是一个基于 promise 的网络请求库。

关于跨域的问题,其实对于现在 前后端分离的开发模式,早已有了解决方案,我使用的是 node 的代理转发来解决这个问题。

其实原理是通过 node 做了一个请求的代理转发,本地发请求用的是另一个映射的地址,这个请求会被 node 转化成真正的请求地址并发送出去。

具体的步骤如下:(使用的是官方的脚手架搭建的项目)

  1. 在 config 目录下,找到 index.js 文件。
  2. 找到 proxyTable 这个配置,这个其实就是代理的配置,往里面加入如下配置(这个代理可以配置多个,像我使用到的豆瓣的接口有两种类型,我也是配置了两个代理):
'/api': {
  target: 'http://api.douban.com',    //设置你调用的接口域名和端口号.别忘了加http
  changeOrigin: true,   //允许跨域
  pathRewrite: {
    '^/api': ''
    // '/'这里理解成用‘/api’代替target里面的地址,后面组件中我们掉接口时直接用api代替。比如我要调用'http://api.douban.com/v2/movie/top250',直接写‘/api/v2/movie/top250’即可
  }
  1. 当使用到这个地址发请求的时候就使用配置的代理前缀就可以了,就像/api/v2/movie/nowplaying?apikey=xxx 这样就可以了。

关于 webpack

其实在这个时候,还看了挺多的关于 webpack 的内容,但是然并卵,直接官方的脚手架就可以直接搭起项目了,还有 热更新,route 等等等,真是美滋滋,哈哈哈。

webpack 是我接触过的第一个前端打包构建工具,之前就是直接写原生 html + css + js ,根本没有用过这个东西,也不知道还有这种东西,虽然之前有写过 react native 但是没有注意到这个东西。

webpack 的意义

就如官网所显示的,webpack 最重要的功能就是打包,可以把几乎所有的资源都打包在一起,减少体积和网络请求次数。

我觉得 webpack 更加重要的功能是 预处理 。(当然,官网上说的 “打包” 也有预处理的意思)webpack 支持使用 loader 和 插件 来对资源进行预处理,和构建,这样就有很多的可能性了。这样我们就可以自己定义代码的规则和风格了。

比如:

  1. 扩展格式支持。 像 Scss , TypeScript 等这些无法直接给浏览器解析的格式,通过 响应的 loader 就可以转化成 浏览器能够支持的 css 和 JavaScript 了。Vue 的单文件也是一个例子。
  2. 兼容处理。 还有 es6 等比较先进的语法,有些旧的浏览器还是不支持的,使用 webpack 打包的时候也可以转化成 es5 的语法。

这样一处理,开发者就在写代码的时候就可以省很多力气了。和以前相比简直就是爽的飞起了。

其他具体的用法这里就不详说了,因为我们今天的重点内容不是这个!(认真脸)(其实是因为我忘得差不多了。。。。。)

Vue 单文件

一开始也是没有注意到有这个东西,但是越写越觉得不对劲。这么多东西(模板,js,css)都写在一起,或者拆开写,需要的时候再组合到一起。这么写还是太麻烦了。要不就不够解耦,或者不够内聚。

Vue 单文件就横空出世了!!!

Vue 单文件是后缀为 .vue 格式的文件。

主要分为三个部分:

Template :模板

script :js,其实主要就是 export vue 对象

style : css,如果配置了 scoped 就只有在当前组件中生效。这里还支持 scss 等扩展语法。需要在 webpack 里面配置 sass-loadernode-sass 这两个 loader,在 style 配置 lang="scss 就行了。

关于 scss

这个也是一个好东西呀。使 css 也具有一部分程序的功能,支持 变量、嵌套、继承等。写起来也是舒服很多。

关于官方的 cli

项目结构大概如下:

忽略掉一些不重要的东西后,大概是这个样子:

|- build    (编译配置,webpack配置)
|- config   (项目配置)
|- src
    |- assets   (资源文件夹)
    |- compoments (组件)
    |- App.vue  (根组件)
    |- main.js  (构建的入口)
|- index.html (html 的入口)

先看一下 index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>test</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

很简单,body 里面就是一个div ,id 为 app。再看一下 main.js

import Vue from 'vue'
import App from './App'
import router from './router'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router, //相当于 router:router ,配置路由
  components: { App },
  template: '<App/>'
})

这个也很简单,APP 组件 挂载到 #app 上面,然后把 .router 路由也挂载进来,就做了这两件事。接下来我们看一下 APP 这个组件吧。

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <router-view/><!-- 路由出口 -->
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

模板里面直接就是一个 img 加 路由 就没了,我们再回过头看一下 路由 吧,在 .router 文件夹的 index.js 文件

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    }
  ]
})

这里路由只配置了 一个地址 ,就是根地址,使用的组件是 HelloWorld,那我们再看一下这个 组件

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h2>Essential Links</h2>
    <ul>
      <li>
        <a
          href="https://vuejs.org"
          target="_blank"
        >
          Core Docs
        </a>
      </li>
      <li>
        <a
          href="https://forum.vuejs.org"
          target="_blank"
        >
          Forum
        </a>
      </li>
      <li>
        <a
          href="https://chat.vuejs.org"
          target="_blank"
        >
          Community Chat
        </a>
      </li>
      <li>
        <a
          href="https://twitter.com/vuejs"
          target="_blank"
        >
          Twitter
        </a>
      </li>
      <br>
      <li>
        <a
          href="http://vuejs-templates.github.io/webpack/"
          target="_blank"
        >
          Docs for This Template
        </a>
      </li>
    </ul>
    <h2>Ecosystem</h2>
    <ul>
      <li>
        <a
          href="http://router.vuejs.org/"
          target="_blank"
        >
          vue-router
        </a>
      </li>
      <li>
        <a
          href="http://vuex.vuejs.org/"
          target="_blank"
        >
          vuex
        </a>
      </li>
      <li>
        <a
          href="http://vue-loader.vuejs.org/"
          target="_blank"
        >
          vue-loader
        </a>
      </li>
      <li>
        <a
          href="https://github.com/vuejs/awesome-vue"
          target="_blank"
        >
          awesome-vue
        </a>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

这个也没什么好说的,就是渲染了一个界面。但是到这里,我们就大概明白了这个结构是怎么回事了。

后面我们要修改的时候,一般就是把 路由里面的 / 路径的映射换成我们的组件就可以了。

Vue 基础

终于说到 vue 的基础了,前排的同学要睡着了。

先看图!

官方的文档中,赫然写着说 我们不是完全遵循 MVVM 模型! 但是对于我这种小菜鸡来说,我现在看到的还就是 MVVM ,而且大部分情况也确实是 MVVM 。我们开发不需要管数据怎么跟视图绑定,我们只要管理好数据就可以了,其他的交给框架去做!

data

这个就是 Vue 的数据,只要是放在这里面的数据更新了,视图里面引用到的相应数据就会更新并且重新渲染。

我们可以直接这么写

var vm = new Vue{
    data : {
        abc: 'abc'
    }
}

这样就创建了一个 Vue 实例。当然关这样写是没有用到,vue 还没有挂载到 DOM 节点上,我们还需要这样

  <html>
    <body>
      <div id="app">
        <p>{{abc}}</p>
      </div>
    </body>
  </html>
var vm = new Vue{
    data : {
        el : '#app'
        abc: 'abc'
    }
}

后面,我们修改数据的时候,直接vm.abc='def' 页面中的显示的 abc 就会变成 def

这就完了。

要注意的是在 .vue 里面,data 是要返回一个函数。

还是推荐使用 .vue 单文件

但是,很多时候我们不会直接把 要加载的 vue 视图放到 DOM 里面去,而且通过 template,因为 template 不会直接被加载出来。

或者,直接使用 vue 文件来写更加痛快,骚年,不要挣扎了,跟我走吧。

其实 vue-loader 把 .vue 中的 template 节点,用 render 函数把视图渲染出来的。

怎么写?

那些 if else ,for ,bindon 这些东西就不讲了,也没什么好讲的,看一下官方文档就很舒服了。

其实写 vue 跟写原生的 html 和前端最不一样的就是思维要转变过来,转变过来之后,思路就很清晰了。尽量不要去操作 DOM 元素!交给 Vue 去做。

就说一下用的比较多的几个东西吧。(说的这几个东西都是在 .vue 中的 export 的对象的属性)

components : 导入其他的组件,使用的时候要在里面声明。之前想要在一个 组件里面引用另一个组件,真的是不知道怎么弄,通过 import 进来了也没用,需要在这里注册一下。

mounted:这个是生命周期的一个钩子,还有其他一些钩子,会在 vue 实例挂载上去后调用,我一般会在这里做网络请求或者,一些初始化的工作。

methods:methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的 this 自动绑定为 Vue 实例。而且文档中也说明,不要用箭头函数,否则会报 undefined。一开始的时候,本来是使用 computed 的,但是不能传参,不好用。

watch:感觉这个东西就很厉害了,可以监控数据的变化。这样我们就只要触发一个事件,就可以唤起整个事件流了,就像多米诺骨牌一样。但是别滥用。

还有,有些时候需要使用箭头函数来使 this 对象指向 vue 实例,这样方便我们操作。比如像 setIntervalpromise 等。

在操作 data 里面的数组的时候,直接 array[2]='222' , 这样是不会触发更新的。但是可以通过 push(),pop()等这些方法是会触发更新的,感觉有点像 java 中的指针引用原理一样。