Vue核心知识点

一、vue.config.js 基本配置

module.exports = {
  // 基本路径 cli3.3以前版本用baseUrl
  publicPath: '/',
  // 输出文件目录
  outputDir: 'dist',
  // 用于嵌套生成的静态资源
  assetsDir: '',
  // 生产环境sourMap
  productionSourceMap: false,
  // webpack配置
  configureWebpack: () => {},
  chainWebpack: () => {},
  
  // css相关配置
  css: {
    // 启动css modules
    modules: false,
    // 是否使用css分离插件
    extract: true,
    // 开启 css sourcemaps?
    sourceMap: false,
    // css 预处理器配置项
    loaderOptions: {}
  },
  
  // webpack-dev-server 相关配置
  devServer: {
    host: '0.0.0.0',
    port: 8080,
    proxy: {} // 设置代理
  },
  // 第三方插件配置
  pluginOptions: {
    // ...
  }
}

二、vue组件间传值

1. 父子组件传值

(1) props(父传子) / $emit(子传父)

(2) $parent / $children

// App => Father => Child
// Father.vue
mounted () {
  console.log(this.$children[0].val) // 访问子组件 Child 的某个数据(子传父)
  console.log(this.$parent.val) // 访问父组件 App 的某个数据(父传子)
  console.log(this.$parent.handleClick) // 也可以是某个方法
}

(3) $refs(访问具体DOM节点)

ref 后面自定义节点名称,从而实现在 js 中访问,访问的方式是 this.$refs.自定义名称

<!-- Father.vue -->
<child ref="child"></child>
// Father.vue
mounted () {
  console.log(this.$refs.child)
}

2. 非父子组件传值

(1) 事件总线

原理:建立一个公有的 js 文件,专门来传递消息。

// 在 util 文件夹下新建 bus.js
import Vue from 'vue'
export default new Vue()

// 在需要传递消息或者接收消息的地方引入
import bus from './bus.js'

// 点击事件中传递自定义事件,发送消息
bus.$emit('msg', val)

// 监听事件,接收消息
bus.$on('msg', val => {
  console.log(val)
})

(2) $attrs / $listeners

爷传孙:$attrs 将父组件中不包含 props 的属性传入子组件,通常配合 inheritAttrs 选项一起使用。

// 祖孙组件传值 App => Father => Child
// App.vue 绑定属性将祖辈组件中 msg、title 传递给父组件
<father :msg="msg" :title="title"></father>
// Father.vue 在父组件上绑定 v-bind="$attrs" 
<child v-bind="$attrs"></child>
// Child.vue 孙子组件获取到祖父组件的数据
mounted () {
  console.log(this.$attrs)
}

$listeners 监听子组件中数据变化,传递给父组件。(略)

(3) vuex

见第四章、第五章


三、Vue Router

1. 路由的基本配置

(1) router 配置(路由懒加载)

// index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    {
      path: '/home',
      name: 'home',
      component: () => import('../components/Home.vue') // 懒加载:动态导入,按需加载
    }
  ]
})

export default router

(2) 路由视图

App.vue,在合适的位置加上以下代码:

<router-view></router-view>

image-20200714115538669

2. 路由的跳转

<router-link to="/home">跳转</router-link>
<router-view></router-view>

(2) 编程式导航 this.$router.push( { ... } )

<button @click="handleGo">js 跳转</button>
<router-view></router-view>
methods: {
  handleGo () {
    this.$router.push({
      path: 'home'
    })
  },
  // handleGo () {
  //  this.$router.push({
  //    name: 'home'
  //  })
  // }
}

3. 动态路由

(1) 基本配置

// router 目录下的 index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    {
      path: '/home/:id', // 动态路由,id 是自定义的参数名
      name: 'home',
      component: () => import('../components/Home.vue')
    }
  ]
})

export default router

通过 $route.params.动态参数名 可以访问到动态参数:

<!-- Home.vue -->
<template>
  <div>
    <h2>Home 页</h2>
    <p>路由动态参数:{{$route.params.id}}</p>
  </div>
</template>

(2) 编程式导航的传参

传参有两种形式,

  • 一种是查询字段的传参。(path + query)
// App.vue (path + query)
methods: {
  handleGo () {
    this.$router.push({
    	path: 'home',
    	query: {
        name: 'jack',
        gender: 'male',
        age: 18
      }
		})
  }
}

点击跳转按钮后,数据被传递到了 url 中:

image-20200714125955474

  • 一种是动态路由参数的传参。(name + params)
// App.vue (name + params)
methods: {
  handleGo () {
    this.$router.push({
      name: 'home',
      params: {
        id: 123
      }
    })
  }
}

image-20200714130306088

4. 嵌套路由

在 Home 页的一个区域显示一个路由页面,这个新的路由页面属于 home 路由,因此就要将它写在home路由规则下。

Home.vue
	|- Child.vue

(1) 路由规则配置

在 home 路由下添加 children 选项,配置新的路由规则。

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    {
      path: '/home/:id',
      name: 'home',
      component: () => import('../components/Home.vue'),
      children: [ // 新路由 Child.vue 写在 children 选项下
        {
          path: '/child',
          component: () => import('../components/Child.vue')
        }
      ]
    }
  ]
})

export default router

(2) 路由视图

router-view 也理应写在 Home.vue 下。

<!-- Home.vue -->
<template>
  <div>
    <h2>Home 页</h2>
    <p>路由动态参数:{{$route.params.id}}</p>
    
    <!-- Child.vue 的视图将会在此处展示 -->
    <router-view></router-view>
    
  </div>
</template>

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

<style lang='stylus' scoped>

</style>

5. 导航守卫

在 main.js 中加入以下代码:

// main.js
router.beforeEach((to, from, next) => {
  console.log('从这出发:', from.path)
  console.log('到达此处:', to.path)
  next()
})

在 '/' 处点击按钮后,跳转到 '/home/123'

image-20200714131345920


四、Vuex 基础用法

公共数据仓库。

image-20200714131651415

  • State
    • 数据
  • Mutations
    • 数据怎么变(同步)
  • Actions
    • 异步改变

1. store 仓库创建

// store 目录下的 index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  // 公共数据
  state: {
    count: 0
  },
  
  // 数据怎么变(同步):每个方法下都有一个参数 state 可以访问到公共数据
  mutations: {
    add (state) {
      state.count++
    },
    decrese (state) {
      state.count--
    }
  },
  
  // 异步修改数据:方法下有一个形参 context,通过 context.commit('变更方法名') 来提交变更方法,告诉它数据怎么变
  actions: {
    // 模拟异步操作
    delayAdd (context) {
      // 一秒后,调用 add 方法
      setTimeout(() => {
        context.commit('add')
      }, 1000)
    }
  }
})

2. 在视图中使用公共数据

(1) 常规方式

在需要用到公共数据的地方,通过计算属性引进 store 仓库中数据。

通过 this.$store.state.数据名 可以获取到公共数据。

// vue 实例
computed: {
  count () {
    return this.$store.state.count
  }
},
<template>
  <div>
    <p>公共数据来了!它是:===> <span style="color:red">{{count}}</span></p>
  </div>
</template>

(2) 辅助函数:mapState

https://vuex.vuejs.org/zh/guide/state.html#mapstate-%E8%BE%85%E5%8A%A9%E5%87%BD%E6%95%B0

当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。(注意,放在计算属性中!)

import { mapState } from 'vuex' // 首先导入辅助函数 mapState

export default {
  computed: {
    ...mapState({ // 通过展开运算符将对象展开
      // 传字符串参数 'count' 等同于 `state => state.count`
      count: 'count'
      
      // 箭头函数可使代码更简练
    	// count: state => state.count
    })
  }
}

关于展开运算符:https://blog.csdn.net/adsadadaddadasda/article/details/79391881

3. 触发 Mutations 修改数据(同步)

(1) 修改数据 this.$store.commit('xxx')

<!-- html -->
<p>公共数据来了!它是:===> <span style="color:red">{{count}}</span></p>
<button @click="handleAdd">改变公共数据 (add)</button>
// vue 实例
// vue 组件 => commit('变更方法名') => Mutations => state => vue 组件
methods: {
  handleAdd () {
    this.$store.commit('add') // 此时触发的就是 store 仓库中 Mutations 下的 add 方法
  }
}
// store 中的变更方法名
mutations: {
  add (state) {
    state.count++
  },
  decrese (state) {
    state.count--
  }
}

(2) 辅助函数:mapMutation

https://vuex.vuejs.org/zh/guide/mutations.html#%E5%9C%A8%E7%BB%84%E4%BB%B6%E4%B8%AD%E6%8F%90%E4%BA%A4-mutation

你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。(注意,放在 methods 中!)

import { mapMutations } from 'vuex' // 首先导入辅助函数 mapMutations

export default {
  methods: {
    ...mapMutations({
      handleAdd: 'add' // // 将 this.handleAdd() 映射为 this.$store.commit('add')
    })
  }
}

4. 触发 Actions 修改数据(一般为异步)

(1) 修改数据 this.$store.dispatch('xxx')

<!-- html -->
<p>公共数据来了!它是:===> <span style="color:red">{{count}}</span></p>
<button @click="handleAdd">改变公共数据 (add)</button>
// vue 实例
// vue 组件 => dispatch('异步方法名') => Actions => commit('变更方法名') => Mutations => vue 组件
methods: {
  handleAdd () {
    this.$store.dispatch('delayAdd')
  }
}
// store 中的 actions 异步方法名
actions: {
  delayAdd (context) {
    // 一秒后,调用 Mutations 中的 add 方法
    setTimeout(() => {
      context.commit('add')
    }, 1000)
  }
}

(2) 辅助函数:mapActions

https://vuex.vuejs.org/zh/guide/actions.html#%E5%9C%A8%E7%BB%84%E4%BB%B6%E4%B8%AD%E5%88%86%E5%8F%91-action

你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store(注意,放在 methods 中!)

import { mapActions } from 'vuex'

export default {
  methods: {
    ...mapActions({
      handleAdd: 'delayAdd' // // 将 this.handleAdd() 映射为 this.$store.dispatch('delayAdd')
    })
  }
}

五、Vuex 高级用法

1. Vuex 中的计算属性:Getters

getters 中的数据依赖于 state 中的数据。

// store
state: {
  count: 0
},
getters: {
  doubleCount (state) {
    return state.count * 2
  }
}

(1) 常规方式

<!-- html -->
<p>公共数据来了!它是:===> <span style="color:red">state: {{count}}</span></p>
<p>公共数据来了!它是:===> <span style="color:green">getters: {{doubleCount}}</span></p>
<button @click="handleAdd">改变公共数据 (add)</button>
// vue 实例
computed: {
	// ...
  doubleCount () {
    return this.$store.getters.doubleCount
  }
}

当点击按钮时,state.count++,因为 doubleCount 依赖于 state.count,因此会同时发生计算。

(2) 辅助函数:mapGetters

https://vuex.vuejs.org/zh/guide/getters.html#mapgetters-%E8%BE%85%E5%8A%A9%E5%87%BD%E6%95%B0

mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性

import { mapGetters } from 'vuex'

export default {
  computed: {
    // ...mapGetters(['doubleCount']) // 参数名和方法名一致
    
    ...mapGetters({
      doubleCount: 'doubleCount'
    })
  }
}

2. 模块化:Modules

各个模块各司其职,管理自己的数据。

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。

https://vuex.vuejs.org/zh/guide/modules.html#module

(1) 模块分离

  • 在 store 文件夹下新建一个 js 文件(模块),将这个模块起名为 handleCount。
// handleCount.js
// handleCount模块
export default {
  state: {
    count: 0
  },
  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  },
  mutations: {
    add (state) {
      state.count++
    },
    decrese (state) {
      state.count--
    }
  },
  actions: {
    delayAdd (context) {
      // 一秒后,调用 add 方法
      setTimeout(() => {
        context.commit('add')
      }, 1000)
    }
  }
}
  • 引入 index.js 主模块中
import Vue from 'vue'
import Vuex from 'vuex'
import handleCount from './handleCount' // 引入模块 handleCount

Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    handleCount
  }
})

因为使用了模块,所以原来的 state 指向都会发生变化。

例如访问 count 数据,不再是 this.$store.state.count,而是 this.$store.state.handleCount.count。(this.$store.state.模块名.数据名)

  • 使用箭头函数
// vue 实例中的 computed 选项
...mapState({
  count: state => state.handleCount.count
})

(2) 命名空间

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

https://vuex.vuejs.org/zh/guide/modules.html#%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4

// handleCount 模块
export default {
  namespaced: true, // 添加命名空间字段,其他照旧
  // ...
}

当添加了命名空间选项时,所有的辅助函数的映射效果就会发生错误,这时要手动调整过来:

// 原代码:
// computed
...mapGetters({
  doubleCount: 'doubleCount'
})
// methods
...mapActions({
  handleAdd: 'delayAdd'
})

// <=============================================================================================>

// 调整后:
// computed
...mapGetters({
  doubleCount: 'handleCount/doubleCount'
})
// methods
...mapActions({
  handleAdd: 'handleCount/delayAdd'
})

关于命名空间:https://blog.csdn.net/lzb348110175/article/details/89387495


六、Element UI

1. 安装依赖

cnpm i element-ui --save

--save 表示依赖包被安装到了生产环境中。(简写 -S)

 i  是 install 的简写
-S 就是 --save 的简写
-D 就是 --save-dev 的简写

npm i module_name -S = > npm install module_name --save 写入到 dependencies 对象

npm i module_name -D => npm install module_name --save-dev 写入到 devDependencies 对象

npm i module_name -g 全局安装

关于指令:https://www.cnblogs.com/del88/p/13272767.html

2. 引入 Element

// main.js
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import App from './App.vue'

Vue.use(ElementUI)

new Vue({
  el: '#app',
  render: h => h(App)
})

3. 布局组件使用

<h3>div块 4等分布局</h3>
<el-row :gutter="20">
  <el-col :span='6'><div class="content">1</div></el-col>
  <el-col :span='6'><div class="content">2</div></el-col>
  <el-col :span='6'><div class="content">3</div></el-col>
  <el-col :span='6'><div class="content">4</div></el-col>
</el-row>

<h3>整块页面布局</h3>
<el-container>
	<el-header>Header</el-header>
	<el-main>Main</el-main>
	<el-footer>Footer</el-footer>
</el-container>

gutter 表示间隔,span 表示占用份数。(一行共 24 份)

el-header el-aside el-main el-footer 的父容器只能是 el-container,el-container 的子容器也只能是前四者。


七、弹出类型组件

1. Dialog 对话框

https://element.eleme.cn/2.0/#/zh-CN/component/dialog#dialog-dui-hua-kuang

<template>
  <div>
    <el-button type="text" @click="dialogVisible = true">点击打开 Dialog</el-button>

    <el-dialog title="提示" :visible.sync="dialogVisible" width="30%" :before-close="handleClose">
      <span>这是一段信息</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: 'pop',
  data () {
    return {
      dialogVisible: false
    }
  },
  methods: {
    handleClose (done) {
      this.$confirm('确认关闭?')
        .then(_ => {
          done()
        })
        .catch(_ => {})
    }
  }
}
</script>

<style lang='stylus' scoped></style>

2. Popover 弹出框

https://element.eleme.cn/2.0/#/zh-CN/component/popover#popover-dan-chu-kuang

<el-popover
  ref="popover1"
  placement="top-start"
  title="标题"
  width="200"
  trigger="hover"
  content="这是一段内容,这是一段内容,这是一段内容,这是一段内容。">
</el-popover>
<el-button v-popover:popover1>hover 激活</el-button>

八、表格组件

1. 基础表格

<el-table
	:data="tableData"
	style="width: 100%"
	height="500"
	border
>
  <el-table-column
		prop="date"
		label="日期"
     width="180">
  </el-table-column>
  <el-table-column
		prop="name"
		label="姓名"
		width="180">
  </el-table-column>
  <el-table-column
		prop="address"
		label="地址">
  </el-table-column>

  <el-table-column label="操作">
    <template slot-scope="scope">
      <el-button
				size="mini"
         @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
      <el-button
				size="mini"
         type="danger"
         @click="handleDelete(scope.$index, scope.row)">删除</el-button>
    </template>
  </el-table-column>
</el-table>
export default {
  name: 'tableList',
  data () {
    return {
      tableData: [{
        date: '2016-05-02',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1518 弄'
      }, {
        date: '2016-05-04',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1517 弄'
      }, {
        date: '2016-05-01',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1519 弄'
      }, {
        date: '2016-05-03',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1516 弄'
      }]
    }
  },
  methods: {
    handleEdit (index, row) {
      console.log(index, row)
    },
    handleDelete (index, row) {
      console.log(index, row)
    }
  }
}

2. el-table 表格常用属性

属性名 作用
height 给表格设置高度,同时固定表头。
show-header 设置是否显示表头。
row-class-name 设置一个函数或者固定的名字作为行的类名。
border 是否显示表格竖直方向的边框,设置后可通过改变边框来设置列宽。

3. el-column 列常用属性

属性名 作用
label 当前列的表头名称
prop 传入的表格 json 数据的 key 值
show-overflow-tooltip 是否设置文字超出列宽时悬浮显示完整内容

4. 通过 v-for 封装更好的表格

<el-table :data='tableData'>
  <el-table-column v-for="(val, key) of tableLabel" :key='key' :prop='key' :label='val'></el-table-column>
  
  <el-table-column label="操作">
    <template slot-scope="scope">
      <el-button
				size="mini"
         @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
      <el-button
         size="mini"
         type="danger"
         @click="handleDelete(scope.$index, scope.row)">删除</el-button>
    </template>
  </el-table-column>
</el-table>
export default {
  name: 'tableList',
  data () {
    return {
      tableData: [{
        date: '2016-05-02',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1518 弄'
      }, {
        date: '2016-05-04',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1517 弄'
      }, {
        date: '2016-05-01',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1519 弄'
      }, {
        date: '2016-05-03',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1516 弄'
      }],
      tableLabel: {
        date: '日期',
        name: '姓名',
        address: '地址'
      }
    }
  },
  methods: {
    handleEdit (index, row) {
      console.log(index, row)
    },
    handleDelete (index, row) {
      console.log(index, row)
    }
  }
}
posted @ 2020-07-15 20:44  见嘉于世  阅读(1011)  评论(0编辑  收藏  举报