# Vue

> Vue学习过程中遇到的一些小tips

1、组件化

Vue 的组件可以分为全局组件和局部组件

全局组件：声明好后可以在全局使用
局部组件：只可以在当前模块使用

```
//全局组件
Vue.componetns('todo-item',{
    template:'<li> { {content} } </li>',
    prop:['content']
});
//局部组件
var HobbyItem = {
   template:"<li>hobby</li>"
}

new Vue({
    el:"#test",
    components:{
        'todo-item':HobbyItem
    }
});
```

2、模块、实例

每一个模块都是 Vue 的一个实例，也就是说可以向每一个模版中像写 Vue 的实例一样进行开发。

3、父组件与子组件通信用 props 传递

4、子组件向外触发事件 this.$emit(事件名,参数列表)

5、template 模版下最外层只可有一个根元素
```
<template>
    <div>
        <p></p>
        ...
    </div>
</template>

```

6、单独在 .vue 文件中，data 是作为函数存在的。

```

data: function(){

}

data () {

}

```

7、在 vue-cli 脚手架中，组件的声明方式

```
export default{
    components : {
        'todo-item':Todo-Item
    }
}

components:['Todo-item']

```

8、style 中可以声明样式作用域
style 同名的样式不会对其他组件有影响

9、在用 Vue 框架在开发的时候一般都会用 **Vue-cli** 脚手架，但是这样经过 webpack 打包后源代码会被打包成 dist 目录下的 bundle.js，此时的代码是被压缩处理过的，且经过 webpack 的处理各个代码模块的逻辑，代码可读性不是很好且不可调试，此时有些人在开发阶段就会需要调试？此时怎么办呢

慌不要慌，小哥哥带你 hold 住全场。
* 脚手架已经帮你处理好了这块需求了，看下图

![配置图例](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/QQ20180602-210826@2x.png)

* 有些大佬不要脚手架，喜欢自己初始化项目，用 npm 挨个安装所需要的依赖。然后自己配置 webpack 的 options。需要调试的话，需要做下面的配置

    ```
    config.devtool = '#cheap-module-eval-source-map'

    ```

这样你就可以在浏览器当中像写普通的 JS 一样进行调试代码了。比如

![调试界面](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/QQ20180602-211328@2x.png)


10、Vue 中 **<template>...</template>** 底层的做法是 ** render()**方法， 对 template 中的元素依次遍历创造节点

11、使用 stylus 写 css 的时候需要告诉 webpack如何处理代码，所以
```
<style lang="stylus">
...
</style>
```

12、在使用 v-for 或 jsx 的map方法时候需要在生成的节点设置一个唯一的**key**作为标识，这样下次渲染的时候就可以根据 key 值判断，是否需要刷新 dom ，提高了效率

13、props 的另一种写法
```
props:[
    todos:{
        type:Array,
        required:true
    }
]
```

14、css 单独打包，缓存下来，提高程序的体验。需要使用**extrat-text-webpack-plugin**

15、业务代码与框架代码如果打包在一起，后期维护很麻烦（因为框架代码基本不需要很快更新，但是业务代码经常更新），所以应该拆分出来，单独打包。将框架代码方便缓存。

16、webpack 的2个概念：
 hash： 各个文件生成的**hash**值相同
 chunkhash：不同文件（模块）生成的**hash**值不同
 

- Vue 使用官方组件（比如 vue-resource）
   - 先引入
   - 再 use（必须在 main.js 中）

```
import VueResource from 'vue-resource'
Vue.use(VueResource);
```

- 第三方组件

  - 哪里用，哪里引入（比如 axios）

- npm install 组件名 --save 

  将安装的组件名称的依赖写入 package.json

- 网络请求组件

  - vue-resource
  - axios
  - fetch-jsonp

- 父组件给子组件传值

  - 父组件在调用子组件的时候传递

  - 子组件 声明 porps

  - 同时可以在子组件接收的时候验证传值的正确性

    ```
    props:['name','age']
    
    props:{
        'name':String,
        'age':Number
    }
    ```

    

- 父组件给子组件传递方法

  ```
  //父
  <script>
  	export default{
          
          methods:{
              run(){
                  alert("run");
              }
          }
  	}
  </script>
  
  <v-header :run="run()"></v-header>
  
  //子
  export default{
    	props:["run"],
  	methods:{
  		test(){
          	run();
  		}
  	}  
  }
  ```

- 子组件可以通过上面传递值给父组件

- 父组件可以将自身传递给子组件。所以可以在子组件里面访问父组件的属性和执行父组件的方法

- 父组件主动获取子组件的数据和方法

  - 在调用子组件的时候给子组件定义一个 ref

      ```
      <v-heaer ref="header"></v-header>
      ```

  - 在父组件里面通过**this.$refs.header.属性** 和 **this.$refs.header.方法** 

  - 子组件里面获取父组件的属性和方法 **this.$parent.属性** 和**this.$parent.方法**

- 非父子组件间的传值

  - 先定义1个中间的组件
  - 在需要暴露数据的一方。import 中间组件，调用 $emit(事件名称,数据)
  - 在需要接受数据的一方。import 中间组件，调用 $on(事件名称,function(data){ //... })

  ```
  //中间组件
  import Vue from 'Vue';
  var VueEvent = new Vue();
  export default = Vue;
  
  //Home.vue(传递数据给 News.vue)
  import VueEvent from '../model/VueEvent.js';
  
  VueEvent.$emit("postData",{"name":"杭城小刘"});
  
  //News.vue(接收数据)
  import VueEvent from '../model/VueEvent.js';
  
  new Vue({
      mounted(){
          VueEvent.$on("postData",function(data){
      		console.log("从Home组件接收到的数据："+data);
  		});
      }
  });
  ```

- 路由的使用规则

  - 创建、引用组件（main.js）

    ```
    import Home form './Components/Home.vue'
    import News form './Components/News.vue'
    ```

  - 导入 vue-router 并 use（main.js）

    ```
    import VueRouter from 'vue-router'
    Vue.use(VueRouter);
    ```

  - 配置路由（main.js）

    ```
    const routes = [
        {path:"/home",component:"Home"},
        {path:"/news",component:"News"},
    ];
    ```

  - 实例化 router（main.js）

    ```
    const router = new VueRouter({
        routes
        //等于 routes:routes,只有当key和value一致的时候可以简写
    });
    ```

  - 挂载 router（main.js）

    ```
    var vue = new Vue({
        el:"#app",
        router,
        data(){
            return {
                msg:"root components"
            }
        }
    });
    ```

  - 在模版里面放上路由的出口。将 <router-view></router-view> 写到根组件上 (App.vue)

    ```
    //App.vue
    <router-view></router-view>
    ```

- 要实现类似导航效果，可以使用 **<router-link></router-link>**

  ```
  <router-link to="/home">首页</router-link>
  <router-link to="/news">新闻</router-link>
  ```

- 默认首页

  ```
  const routes = [
      {path:"/home",component:"Home"},
      {path:"/news",component:"News"},
      {path:"*",redirect:"News"}	//默认跳转到首页
  ];
  ```

- Vue 动态路由和 Get 传值

  ```
  <ul>
  	<li v-for="(item,key) in news"><router-link to="/content"></router-link>{ {item} }</li>
  </ul>
  
  //新增加的 router-link 需要在路由配置里面添加配置项。
  const routes = [
      {path:"/home",component:"Home"},
      {path:"/news",component:"News"}, 
      {path:"/content",component:"Content"},
      {path:"*",redirect:"News"}	//默认跳转到首页
  ];
  ```

   - 上面的写法是静态路由，也就是不能传递参数

   - 那么什么是动态路由，也就是可以传递参数

     ```
     //新增加的 router-link 需要在路由配置里面添加配置项。
     const routes = [
         {path:"/home",component:"Home"},
         {path:"/news",component:"News"}, 
         {path:"/content:newid",component:"Content"},	//动态路由
         {path:"*",redirect:"News"}	//默认跳转到首页
     ];
     ```

  - 子组件页面获取动态路由传递过来的值 

    ```
    this.$route.params
    ```

  - 子组件拿到父组件动态传递过来的值用动态路由

    ```
    <ul>
    	<li v-for="(item,key) in news"><router-link :to="'/content' + key" ></router-link>{ {item} }</li>
    </ul>
    
    const routes = [
        {path:"/home",component:"Home"},
        {path:"/news",component:"News"}, 
        {path:"/content:newid",component:"Content"},	//动态路由
        {path:"*",redirect:"News"}	//默认跳转到首页
    ];
    
    this.$route.params
    ```

  - Get 传值

    ```
    const routes = [
        {path:"/home",component:"Home"},
        {path:"/news",component:"News"}, 
        {path:"/content",component:"Content"},	//动态路由
        {path:"/product",component:"Product"},	//
        {path:"*",redirect:"News"}	//默认跳转到首页
    ];
    
    <ul>
    	<li v-for="(item,key) in news"><router-link :to="'/content?id='+key" ></router-link>{ {item} }</li>
    </ul>
    
    //拿到值
    this.$route.query
    ```

- 编程式导航（JS 跳转控）

  ```
  //直接跳转到某个组件
  this.$route.push({path:'News' });
  //跳转到某个组件并且传递值
  this.$route.push({path:'/content/495'});
  ```

  - 命名路由跳转

    ```
    const routes = [
        {path:"/home",component:"Home"},
        {path:"/news",component:"News"}, 
        {path:"/content",component:"Content",name:'news'},	//动态路由
        {path:"/product",component:"Product"},	//
        {path:"*",redirect:"News"}	//默认跳转到首页
    ];
    //跳转
    this.$router.push({name:'news'});
    ```

    

- vue-router 默认的是 hash 模式， 也就是 127.0.0.1/# 这种。所以我们在初始化 vue-router 的时候可以修改它的模式

  ```
  const VueRouter = new VueRouter({
  	mode: 'history',//hash 模式改为 history 模式
      routs
  })
  ```

- 嵌套路由（比如顶部的菜单栏不变，点击左边的菜单栏实现页面内容的切换）

  ```
  //注册路由嵌套
  const routes = [
      {path:"/home",component:"Home"},
      {path:"/news",component:"News"}, 
      {
      	path:"/user",
      	components:"User",
      	children:[
              {path:'useradd',component:'UserAdd'},
              {path:'userlist',component:'UserList'},
      	]
      }, 
      {path:"/content",component:"Content",name:'news'},	//动态路由
      {path:"/product",component:"Product"},	//
      {path:"*",redirect:"News"}	//默认跳转到首页
  ];
  
  //将<router-view></router-view>放到动态加载的子组件的地方	
  //User.vue
  
  <template>
  	<div>
  		<ul>
  			<router-link to='/useradd'></router-link><li>增加用户</li>
  			<router-link to='/userlist'></router-link><li>用户列表</li>
  		</ul>
  		<router-view></router-view>
  	</div>
  </template>
  ```

  

- Vuex：主要解决不同组件之间的数据共享、数据持久化（不适合与小项目，主要用于大项目）

  - 安装 vuex

  - src 目录下新建一个 vuex 的文件夹

  - vuex 文件夹下面新建一个 store.js 文件

  - 引入 vuex

    ```
    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex);
    ```

  - 定义数据

    ```
    // state 在 vuex 中用于存储数据
    var state = {
        count:1
    }
    ```

  - 定义方法

    ```
    var muations = {
        incCount(){
            ++state.count;
        }
    }
    ```

  - 暴露 vuex

    ```
    const store = new Vuex.Store({
        state,
        mutations
    });
    
    export default store;
    ```

- 使用 vuex(解决不同组件之间数据共享问题；数据持久化)

  - 引入 vuex

    ```
    import store from '../vuex/store.js'
    ```

  - 注册 vuex

    ```
    export default{
        data(){
            return {
                msg: 'Hello'
            }
        },
        store,
        methods:{
            
        } 
    }
    ```

  - 使用

    ```
    //访问属性
    this.$store.state.count
    //触发方法+不带参数
    this.$store.commit('incCount');
    
    //触发方法+不带参数
    this.$store.commit('方法名',参数);
    
    var mutation = {
        addList(state,data{
            state.list = data;
        }
    }
    ```

  - getters 类似于计算属性，改变 state 里面的 count 的值会触发 setters 里面的方法，从而在里面可以获取新的值（做一些逻辑操作）

    ```
    var getters = {
        computedCount: (state) => {
            return state.count*2
        }
    }
    ```

  - action 类似于 mutation ，在外面使用的时候用 this.$state.dispath('方法名')


- 某些页面可能我们只需要切换数据源和样式模版。所以数据源有可能是变化的，页面的模版也是变化的。
   这样子我们可能会用到 `v-if、v-else-if...v-else` 来切换页面的模版。但是在模版里面去 `v-for` 循环展示数据源。  
   早期遇到错误，大概意思是说我们循环动态生成的元素，有 key 重复了，最后查找资料得到解决方案。在判断模版的时候需要使用 `<template v-if="type==1"></template>`
   ```
    <template v-else-if="collectType==4">
        <b-card v-for="(item, index) in qualifications" v-bind:key="'qualification' + index" :title="item.company"
            sub-title="">
        <p class="card-text">
            <em>法定代表人：{ {item.legalperson} }</em>
            <em>成立时间：{ {item.create} }</em>
            <em>注册资本：{ {item.capital} }(万元)</em>
        </p>
        <p v-for="(qualification,index) in item.qualifications" v-bind:key="index" style="font-size:13px;">
            { {qualification} }
        </p>
        </b-card>
    </template>
   ```