Vue-Cli搭建项目

Vue-Cli搭建项目

以下文章是针对vue2的使用介绍

环境搭建

1. 全局安装vue

npm install vue

2. 安装vue的cnpm为淘宝镜像

npm install -g cnpm –registry=https://registry.npm.taobao.org

3. 全局安装vue-cli

npm install --global vue-cli

4. 创建一个基于webpack的项目

vue init webpack vuedemo

5. 安装依赖,运行项目

cd vuedemo 切换到项目根目录
npm install		安装依赖
npm run dev 	运行项目

项目运行成功,默认端口是8080
在这里插入图片描述

项目结构

在这里插入图片描述
项目的根目录下的src下的main.js为项目的入口文件

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'

Vue.config.productionTip = false // vue环境下的生产提示

/* eslint-disable no-new */
new Vue({
  el: '#app', // 绑定根视图
  router,
  components: { App }, // 加载组件
  template: '<App/>' // 使用组件
})

自定义属性

<template>
  <div id="app">
    <!--v-bind:属性名称=属性值-->
    <!--可以简写为:属性名=属性值-->
    <!--p添加自定义属性id="12"-->
    <p :id="msg">123</p>
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      msg: '12'
    }
  }
}
</script>

<style scoped>

</style>

页面样式
在这里插入图片描述

获取自定义属性值

<template>
  <div id="app">
    <!--v-bind:属性名称=属性值-->
    <!--可以简写为:属性名=属性值-->
    <!--p添加自定义属性id="12"-->
    <p msg="msg" @click="getMsg">123</p>
  </div>
</template>
<script>
export default {
  name: 'App',
  data () {
    return {
      msg: '12'
    }
  },
  methods: {
    getMsg: function (e) {
      // 获取属性msg的值
      console.log(e.target.getAttribute('msg'))
    }
  }
}
</script>

<style scoped>

</style>

template

在这里插入图片描述
所以可以在页面使用多个template,通过v-if来指定渲染哪个template包裹的元素

<template>
  <div id="app">
   <p>124</p>
    <template v-if="flag">
      <p>125</p>
    </template>
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      msg: '12',
      flag: false
    }
  },
  methods: {
  }
}
</script>

<style scoped>

</style>

页面样式
在这里插入图片描述

计算属性

对数据进行处理

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})

组件

1、创建组件

HTML、JavaScript、CSS
在项目的src/components下创建vue的组件
在这里插入图片描述

<template>
  <div>我是Hello</div>
</template>

<script>
export default {
  name: 'hello'
}
</script>
<!--scoped:样式的作用域,代表样式只在当前组件中生效-->
<style scoped>

</style>

2、使用组件

在App.vue中使用组件

<template>
  <div id="app">
    <hello></hello>
  </div>
</template>

<script>
/* 导入组件 */
import hello from './components/hello'
export default {
  name: 'App',
  data () {
    return {

    }
  },
  /* 注册组件 */
  components: {hello},
  methods: {
  }
}
</script>

<style scoped>

</style>

页面效果
在这里插入图片描述

父组件给子组件传值

  1. 在父组件页面引用子组件
  2. 在父组件页面上的子组件绑定传递的属性和属性值
  3. 子组件使用props接收父组件传递的属性

父组件页面

<template>
    <div>
      我是父组件
      <child :msg="msg"></child>
    </div>

</template>

<script>
import child from "./child"
    export default {
     name: "parent",
      data(){
       return{
        msg:'父亲给儿子的东西'
       }
      },
      components:{child}
    }
</script>

<style scoped>

</style>

子组件页面

<template>
    <div>我是子组件
      <div>{{msg}}</div>
    </div>

</template>

<script>
    export default {
        name: "child",
      data(){
          return{

          }
      },
      props:{
        msg:{
          type:String,
          required:true
        }
      }
    }
</script>

<style scoped>

</style>

页面显示
在这里插入图片描述

子组件给父组件传值

子组件通过事件向父组件传值

  1. 子组件定义事件触发父组件自定义事件和传值
  2. 父组件自定义事件函数使用形参接收子组件传递的值

子组件

<template>
    <div>我是子组件
      <button @click="sendMsg" type="button">发送信息</button>
    </div>

</template>

<script>
    export default {
        name: "child",
      data(){
          return{
            initMsg:'我是儿子的数据,还要发送给父亲'
          }
      },
      methods:{
        sendMsg(e){
          //儿子准备发送好数据
          this.$emit('getInfo',this.initMsg);
        }
      }
    }
</script>

<style scoped>

</style>

父组件

<template>
    <div>
      <child  @getInfo="getInfo"></child>
      <p>{{initInfo}}</p>
    </div>

</template>

<script>
import child from "./child"
    export default {
     name: "parent",
      data(){
       return{
         initInfo:''
       }
      },
      components:{child},
      methods:{
        getInfo(msg){
          this.initInfo=msg;
        }
      }
    }
</script>

<style scoped>

</style>

页面样式
在这里插入图片描述
点击发送信息按钮,页面样式如下:
在这里插入图片描述

插槽

插槽

<template>
    <div class="container">
      <p>准备实现插槽功能</p>
      <!--组件渲染时,插槽的默认内容,没有提供内容时显示,有内容则不显示默认内容-->
      <slot>为传递单个插槽功能</slot>
    </div>
</template>

<script>
    export default {
      name: "useComponent",
      data(){
        return{

        }
      }
    }
</script>

<style scoped>

</style>

App.vue中引用组件

<template>
  <div id="app">
    <useComponent>
      <!--虽然数据传递给子组件显示,但是,渲染的时候,依然是在父组件中渲染的-->
    </useComponent>
  </div>
</template>

<script>
import parent from "./components/parent";
import useComponent from "./components/useComponent";
export default {
  name: 'App',
  components:{parent,useComponent}
}
</script>

<style>

</style>

具名插槽

< slot > 元素有一个特殊的 attribute:name。这个 attribute 可以用来定义额外的插槽:
一个不带 name 的 < slot > 出口会带有隐含的名字“default”
向具名插槽提供内容的时候,我们可以在一个 < template > 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:

使用步骤
  1. 定义具名插槽
<template>
    <div class="container">
      <!--定义具名插槽的name="hello"-->
      <slot name="hello"></slot>
    </div>
</template>

<script>
    export default {
      name: "useComponent",
      data(){
        return{

        }
      }
    }
</script>

<style scoped>

</style>

  1. 使用具名插槽
<template>
  <div id="app">
    <useComponent>
      <!--为具名插槽name="hello"的插槽提供内容-->
      <p slot="hello">hello的具名插槽</p>
    </useComponent>
  </div>
</template>

<script>
import parent from "./components/parent";
import useComponent from "./components/useComponent";
export default {
  name: 'App',
  components:{parent,useComponent}
}
</script>

<style>

</style>

页面显示
在这里插入图片描述

组件缓存

如果把切换出去的组件保留在内容中,保留它的状态户或避免重新渲染,可以添加一个keep-alive指令参数

组件ct1

<template>
  <div class="ct1">
    ct1
  </div>
</template>

<script>
    export default {
        name: "ct1"
    }
</script>

<style scoped>

</style>

组件ct2

<template>
    <div class="ct2">
      ct2
    </div>
</template>

<script>
    export default {
        name: "ct2"
    }
</script>

<style scoped>

</style>

App.vue

<template>
  <div id="app">
    <button @click="changeEvent">切换</button>
    <keep-alive>
      <component :is="currentView"></component>
    </keep-alive>
  </div>
</template>

<script>
import ct1 from "./components/ct1";
import ct2 from './components/ct2';
export default {
  name: 'App',
  components:{ct1,ct2},
  data(){
    return{
      currentView:ct1,//当前组件为ct1
      flag:true//切换组件的开关
    }
  },
  methods:{
    changeEvent(e){
      if(this.flag==true){
        this.currentView=ct2;
        this.flag=false;
      }else{
        this.currentView=ct1;
        this.flag=true;
      }
    }
  }
}
</script>

<style scoped>
</style>

页面样式
在这里插入图片描述
点击按钮动态切换组件

生命周期函数

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed
<template>
    <div id="app">
        <button @click="changeData">改变</button>
        {{myData}}
    </div>
</template>
<script>
    export default {
      name: "ComponentDemo",
      data(){
       return{
        myData:'改变之前的数据'
       }
      },
      methods:{
        changeData(){
          this.myData='数据改变之后';
        }
      },
      //组件被创建之前
      beforeCreate() {
        console.log('组件被创建之前');
      },
      created() {
        console.log('组件被创建之后');
      },
      //组件被渲染之前
      beforeMount() {
        console.log('组件被渲染之前');
      },
      mounted() {
        console.log('组件被渲染之后');
      },
      //数据改变重新渲染
      beforeUpdate() {
        console.log('数据改变重新渲染之前');
      },
      updated() {
        console.log('数据改变重新渲染之后');
      },
      //组件销毁之前
      beforeDestroy() {
        console.log('组件被销毁之前');
      },
      destroyed() {
        console.log('组件被销毁之后');
      }
    }
</script>
<style scoped>
</style>

页面加载时
在这里插入图片描述
点击按钮
在这里插入图片描述

路由

基础使用

所有的组件通过路由嵌套起来,然后形成页面跳转的效果

  1. 安装
npm install --save vue-router
  1. 编写代码
  • 在main.js中引入router
import VueRouter from 'vue-router'
Vue.use(VueRouter)

-创建Router

// 创建router
var router = new VueRouter({
  routes:[
    {
      path:'/',
      name:'Hello',
      component:HelloWorld
    }
  ]
})
  • 注入router

在src/main.js中注入路由

new Vue({
  el: '#app',
  router, // 注入 router
  components: { App },
  template: '<App/>'
})

在main.js中注入router的完整代码

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
// 引入VueRouter
import VueRouter from "vue-router"
// 引入路由组件
import HelloWorld from "./components/HelloWorld"
Vue.config.productionTip = false
// 使用VueRouter
Vue.use(VueRouter)
// 创建router
var router = new VueRouter({
  routes:[
    {
      path:'/',
      name:'Hello',
      component:HelloWorld
    }
  ]
})
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router, // 挂载router
  components: { App },
  template: '<App/>'
})

  • 显示路由组件
    在App.vue中显示路由组件
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <!--路由显示的位置-->
    <router-view></router-view>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</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>

页面访问
http://localhost:8080/#/
在这里插入图片描述

在vue-cli中写法

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

在main.js中引入并注册

import router from './router' // 引入router(默认引入router文件夹下index.js)
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

在app.vue中使用router-view标签

<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>

项目结构
在这里插入图片描述

router-link

命名路由
  • router-link 比起写死的< a href="" >会好一些
  • 无论是HTML5 history模式还是hash 模式,它的表现行为一致,当切换路由模式,或者在IE9降级使用hash模式,无须任何变动
  • 在HTML5 history 模式下,router-link 会守卫点击事件,让浏览器不再重新加载页面
  • 当在HTML5 history 模式下,使用base 选项之后,所有 to 属性都不需要写(基础路径)了

代码:

前提是在安装路由、在main.js中引入并注册
才可以使用router-link

1. 在vue-cli搭建的项目的router下的index.js中配置路由
export default new Router({
  routes: [
    {
      path: '/hi',
      name: 'hi',
      component: hi
    }
  ]
})
2. 在引用的页面使用命名路由
<!-- :to{name:'',} name对应router/index.js中配置的name
          -->
          <router-link :to="{name:'hi'}">hi</router-link>
          <!--path:对应router/index.js中配置的path-->
          <router-link :to="{path:'/hi'}">hi</router-link>

页面中渲染
router-link 在页面中显示为a链接
在这里插入图片描述
在这里插入图片描述
router-link 在页面中显示为span链接

<!-- :to{name:'',} name对应router/index.js中配置的name tag="span" tag渲染成指定的标签,这里在页面中渲染成span标签
          -->
          <router-link :to="{name:'hi'}" tag="span">hi</router-link>

重定向

在路由的配置文件中配置的路由使用redirect

官网介绍
export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      redirect: 'nav' // 重定向配置
    },
    {
      path: '/nav',
      name: 'nav',
      component: nav
    },
  ]
})

路由嵌套

官网介绍

URL 中各段动态路径也按某种结构对应嵌套的各层组件
使用children
显示的位置:父级在哪里,显示的位置就在哪里

import Vue from 'vue'
import Router from 'vue-router'
import hi from '../components/hi'
import nav from '../nav'
Vue.use(Router)
export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      redirect: 'nav' // 重定向配置
    },
    {
      path: '/nav',
      name: 'nav',
      component: nav,
      children:[ // 嵌套路由
        {
          path: '/hi',
          name: 'hi',
          component: hi
        }
      ]
    }
  ]
})

在引用的页面使用router-view标签

路由参数传递

官网介绍
  1. 配置路由信息
 {
      path: '/hello',
      name: 'hello',
      component: hello
    }
  1. 配置路由跳转信息
  <router-link :to="{name:'hello', params: { userId: 123 }}">hello</router-link>
<router-link :to="{path:'/hello', query: { plain: 'private' }}">hello带查询参数</router-link>

路由参数获取

官网介绍
<template>
  <div>
   <div>hello</div>
    <div>路径:{{$route.path}}</div>
    <div>传递的参数params下的UserId:{{$route.params.userId}}</div>
    <div>传递的查询参数plan:{{ $route.query.plain }}</div>
  </div>
</template>

<script>
export default {
  name: 'hello',
  mounted () {
  }
}
</script>

<style scoped>

</style>

路由高亮

在引用的页面下加入如下代码

.router-link-active{
  /*设置路由激活状态下字体的颜色*/
  color:hotpink; /*粉色*/
}

也可以在vue-cli搭建下的router/index.js中添加路由高亮的全局配置

import Vue from 'vue'
import Router from 'vue-router'
import hi from '../components/hi'
import nav from '../Nav'
import hello from '../components/hello'
Vue.use(Router)
export default new Router({
  // 配置全局的路由激活状态样式
  linkActiveClass: 'active', // active:激活状态下添加的class
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      redirect: 'nav' // 重定向配置
    },
    {
      path: '/nav',
      name: 'nav',
      component: nav,
      children: [
        {
          path: '/hi',
          name: 'hi',
          component: hi
        }
      ]
    },
    {
      path: '/hello',
      name: 'hello',
      component: hello
    }
  ]
})

路由设置单页面title

在main.js中添加如下代码

router.beforeEach((to, from, next) => {
  /* 路由发生变化修改页面title */
  if (to.meta.title) {
    document.title = to.meta.title
  }
  next()
})

在这里插入图片描述
router > index.js

import Vue from 'vue'
import Router from 'vue-router'
import login from '@/page/login/login'
Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/login',
      name: 'login',
      component: login,
      meta: {
        title: '登录'
      }
    }
  ]
})

项目结构
在这里插入图片描述
访问页面路径http://localhost:8080/#/login,显示在router>index.js中配置的标题 登录
在这里插入图片描述

Vue中使用element-ui

按需引入

1、命令行切换到项目的根目录下
cd demo
2、输入cnpm i element-ui -S
3、输入cnpm install babel-plugin-component -D
4、修改 .babelrc
如下

{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime", [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]]
}

5、在项目的根目录下的src下的main.js中按需引入需要的样式
例如引入element-ui中的button,代码如下:

import { Button } from 'element-ui'
// 按需引入elementui
Vue.use(Button)

6、在需要的页面使用button

<template>
  <div class="hello">
    <!--使用element-ui中的button-->
    <el-button>按钮</el-button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
    }
  }
}
</script>

<style>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

完整引入

在main.js中添加

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

完整代码如下:

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
// 完整引入element-ui
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
// 完整引入element-ui
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

引入成功之后,可按照 官网 介绍在要使用的页面进行元素的使用

表单的使用

基本用法

  1. el-form容器,通过model绑定数据
  2. el-form-item容器,通过label绑定标签
  3. 表单组件通过v-model绑定model中的数据
<template>
  <div class="hello">
  <!--inline:设置 inline 属性可以让表单域变为行内的表单域-->
    <el-form inline :model="data">
      <el-form-item label="审批人">
        <el-input v-model="data.user" placeholder="审批人"></el-input>
      </el-form-item>
      <el-form-item>
        <el-select v-model="data.region" placeholder="活动区域">
          <el-option label="区域一" value="上海"></el-option>
          <el-option label="区域二" value="北京"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSubmit">查询</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      data: {
        user: 'sam',
        region: '区域二'
      }
    }
  },
  methods: {
    onSubmit () {
      console.log(this.data)
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

页面显示
点击查询按钮,输出表单内容
在这里插入图片描述

表单校验

  1. 定义校验规则,可以绑定到el-form或el-form-item
<template>
  <div class="hello">
    <!--inline:设置 inline 属性可以让表单域变为行内的表单域
    :rules:绑定校验规则
    -->
    <el-form inline :model="data" :rules="rules">
      <!--prop:user,user等于data函数中声明的校验规则-->
      <el-form-item label="审批人" prop="user">
        <el-input v-model="data.user" placeholder="审批人"></el-input>
      </el-form-item>
      <el-form-item>
        <el-select v-model="data.region" placeholder="活动区域">
          <el-option label="区域一" value="上海"></el-option>
          <el-option label="区域二" value="北京"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSubmit">查询</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    // 自定义校验规则
    const userValidator = (rule, value, callback) => {
      if (value.length > 3) {
        callback()
      } else {
        callback(new Error('用户名长度必须大于3'))
      }
    }
    return {
      data: {
        user: '',
        region: ''
      },
      // 定义表单校验规则
      rules: {
        user: [
          /* trigger:表单值发生改变时进行校验 */
          {required: true, trigger: 'change', message: '用户名必须输入'},
          {validator: userValidator, trigger: 'change'} // 自定义校验规则
        ]
      }
    }
  },
  methods: {
    onSubmit () {
      console.log(this.data)
      if (!this.data.user) {
        // 用户名为空给出提示信息
        this.$message.error('用户名不能为空!')
      } else if (this.data.user.length < 10) {
        this.$message.info('用户名长度不能小于10个字符')
      }
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

页面显示
在这里插入图片描述

elementui的表单校验

<template>
  <div class="hello">
    <!--inline:设置 inline 属性可以让表单域变为行内的表单域
    :rules:绑定校验规则
    -->
    <el-form inline :model="data" :rules="rules"  ref="form">
      <!--prop:user,user等于data函数中声明的校验规则-->
      <el-form-item label="审批人" prop="user">
        <el-input v-model="data.user" placeholder="审批人"></el-input>
      </el-form-item>
      <el-form-item>
        <el-select v-model="data.region" placeholder="活动区域">
          <el-option label="区域一" value="上海"></el-option>
          <el-option label="区域二" value="北京"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSubmit">查询</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    // 自定义校验规则
    const userValidator = (rule, value, callback) => {
      if (value.length > 3) {
        callback()
      } else {
        callback(new Error('用户名长度必须大于3'))
      }
    }
    return {
      data: {
        user: '',
        region: ''
      },
      // 定义表单校验规则
      rules: {
        user: [
          /* trigger:表单值发生改变时进行校验 */
          {required: true, trigger: 'change', message: '用户名必须输入'},
          {validator: userValidator, trigger: 'change'} // 自定义校验规则
        ]
      }
    }
  },
  methods: {
    onSubmit () {
      this.$refs.form.validate((isValid, errors) => {
        // 控制台输出校验规则
        console.log(isValid, errors)
      })
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

页面显示
在这里插入图片描述
表单校验实例

<template>
  <div class="login">
    <div class="loginWrapper">
      <div class="loginMain">
        <div class="links">
          <p class="accountUser">账户密码登录</p>
          <p class="regUser">
            <el-link  :underline="false">
              <router-link to="/register" tag="span" >注册用户</router-link>
            </el-link>
          </p>
        </div>
        <div class="submitForm">
          <el-form ref="form" :model="form"  style="width: 25%;margin: 0 auto;" :rules="rules">
            <el-form-item style="margin-top: 25px;" prop="username">
              <el-input v-model.trim="form.username" placeholder="账户" prefix-icon="el-icon-user" clearable></el-input>
            </el-form-item>
            <el-form-item prop="password">
              <el-input v-model.trim="form.password" placeholder="密码"  prefix-icon="el-icon-lock" clearable type="password"></el-input>
            </el-form-item>
            <el-form-item >
              <el-button type="primary" size="medium" style="width: 100%;" @click="login('form')">登录</el-button>
            </el-form-item>
          </el-form>
        </div>
        
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'login',
  data () {
    return {
      form: {
        username: '', // 账号
        password: '' // 密码
      },
      rules: {
        username: [{required: true, message: '请输入账号'}],
        password: [{required: true, message: '请输入密码'}]
      }
    }
  },
  methods: {
    login (formName) {
      let that = this
      this.$refs[formName].validate((valid) => {
        if (valid) {
          that.$router.push({path: '/'})
        } else {
          return false
        }
      })
    }
  }
}
</script>

<style scoped>
.login{
  height: 100%;
  background: url(/static/img/background.png);
  position: fixed;
  width: 100%;
  background-size: 100% 100%;
}
.loginWrapper{
  position: relative;
  left: 0;
  top: 0;
  padding: 100px 0;
  min-height: 100%;
  box-sizing: border-box;
}
.loginMain{
  margin: 0 auto;
  box-sizing: border-box;
  width: 80%;
}
.loginTitle{
  text-align: center;
  padding: 10px;
}
.cover{
  padding: 130px 0;
}
.biaoti{
  font-size: 26px;
  color: rgba(0, 0, 0, 0.85);
  font-weight: 700;
  display: flex;
  justify-content: center;
}
.accountUser{
  display: inline-block;
  font-size: 16px;
  color: rgba(24, 144, 255, 1);
  position: relative;
  width: 19%;
}
.accountUser:after{
  position: absolute;
  width: 111px;
  height: 2px;
  background: rgba(24, 144, 255, 1);
  content: '';
  top: 24px;
  left: -6px;
}
.regUser{
  display: inline-block;
  font-size: 16px;
}
.links{
  display: flex;
  justify-content: center;
  margin: 15px auto;
}
.forgetPwd{
  float: right;
  padding: 5px;
  margin-bottom: 10px;
}
</style>

el-menu

<template>
  <div class="sidebarWrapper">
    <div class="sideBar">
      <el-menu
        text-color="#FFF"
        background-color="#002040"
        :default-openeds="['10']"
        active-text-color="#0089FF"
        unique-opened
        :default-active="$route.path"
        @open="handleOpen"
        @close="handleClose"
        router
      >
        <el-submenu index="10" >
          <template slot="title">
            <span class="menuIcon"></span>
            <span style="display: inline-block;vertical-align: middle">学习管理</span>
          </template>
            <el-menu-item class="menu-item is-active" index="/jobKind">
              分类
            </el-menu-item>
            <el-menu-item class="menu-item" index="/jobManager">
              <span>管理</span>
            </el-menu-item>
        </el-submenu>
      </el-menu>
    </div>
  </div>
</template>

<script>
export default {
  name: 'sideBar',
  data () {
    return {
    }
  },
  methods: {
    handleOpen (key, keyPath) {
    },
    handleClose (key, keyPath) {
    }
  }
}
</script>

<style>
  .sidebarWrapper{
    position: fixed;
    top: 0px;
    left: 0;
    bottom: 0;
    z-index: 1031;
    width:190px;
    background-color: #002040;
  }
  .logo {
    margin: 10px auto;
    width: 130px;
    display: block;
  }
  .sideBar{
    position: relative;
    z-index: 1;
    width: 190px;
    height: 100%;
    padding-bottom: 15px;
    overflow-x: hidden;
    overflow-y: auto;
  }
  .menu-item {
    padding-left: 48px !important;
  }
  .el-menu{
    border-right: 0;
  }
  .el-submenu__title i{
    color: #FFF;
    font-size: 22px;
    vertical-align: middle;
  }
  .el-submenu__title{
    font-size: 16px;
    background-color: #3888E5!important;
  }
  .el-submenu__icon-arrow{
    margin-top: 0px;
  }
  .menuIcon{
    display: inline-block;
    width: 15%;
    height:45%;
    background: url("../assets/gwxx.png") no-repeat;
    background-size: 100%;
    vertical-align: middle;
  }
</style>

页面样式
在这里插入图片描述

重写element-ui中的Message,实现相同消息提示只弹一个

在main.js中添加如下代码

import ElementUI, {Message} from 'element-ui'
// 为了实现Class的私有属性
const showMessage = Symbol('showMessage')
/**
 *  重写ElementUI的Message
 *  single默认值true,因为项目需求,默认只弹出一个,可以根据实际需要设置
 */
class DonMessage {
  success (options, single = true) {
    this[showMessage]('success', options, single)
  }
  warning (options, single = true) {
    this[showMessage]('warning', options, single)
  }
  info (options, single = true) {
    this[showMessage]('info', options, single)
  }
  error (options, single = true) {
    this[showMessage]('error', options, single)
  }

  [showMessage] (type, options, single) {
    if (single) {
      // 判断是否已存在Message
      if (document.getElementsByClassName('el-message').length === 0) {
        Message[type](options)
      }
    } else {
      Message[type](options)
    }
  }
}
Vue.prototype.$message = new DonMessage()

2.在组件中使用

this.$message.error({
                message: '必填项不能为空',
                type: 'error'
              })

Vue-axios

  • 从浏览器中创建 XMLHttpRequests
  • 从 node.js 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF

使用

axios参考使用地址
1、安装

npm install axios --save

或者使用cnpm安装,命令如下

cnpm install axios --save

2、在src/main.js中添加如下代码

// 引入axios
import Axios from 'axios'
// 挂载到Vue的原型上
Vue.prototype.$axios = Axios

get请求

无参数get请求

//this.$axios.get(url) url:地址
 this.$axios.get('http://www.baidu.com')
      .then(res => {
      //res.data 返回的响应数据
        console.log(res.data)
      })
      .catch(error => {
      //error 发送错误时返回的响应结果
        console.log(error)
      })

get请求传参

this.$axios.get('http://www.baidu.com', {
     //params:传递的参数
      params: {
        wd: 'element'
      }
    })
      .then(res => {
        console.log(res.data)
      })
      .catch(error => {
        console.log(error)
      })

也可以写成如下get请求传参

axios的get请求

 this.$axios({
      method: 'get',
      url: 'http://www.baidu.com',
      params: {
        wd: 'elment'
      }
    })
      .then(res => {
        console.log(res.data)
      })
      .catch(error => {
        console.log(error)
      })

post请求

// axios的post请求接收参数是form-data格式:?name='124'&age=10,后台无法接收,所以使用qs序列化参数
this.$axios.post('http://www.baidu.com', //url地址
      {
        wd: 'elment' //传递的参数
      })
      .then(res => {
        console.log(res.data) //响应结果
      }).catch(error => {
        console.log(error)
      })

关于Vue中,序列化字符串,处理发送请求的参数
使用工具qs来处理参数

使用qs

1、切换到项目根目录下执行

npm install qs --save-dev

命令安装qs
2、在main.js中进行导入qs,交给vue进行使用

// 引入qs
import qs from 'qs'
// qs挂载到Vue的原型上
Vue.prototype.$qs = qs

3、使用
在页面中使用this.$qs来调用

 // axios的post请求接收参数是form-data格式:?wd='element'&age=20
    this.$axios.post('http://www.baidu.com',
      this.$qs.stringify({
        wd: 'elment'
      })
    ).then(function (response) {
        console.log(response)
      }).catch(function (error) {
        console.log(error)
      })

axios的post

this.$axios({
      method: 'post',
      url: 'http://www.baidu.com',
      data: {
        wd: 'element'
      }
    }).then(res => {
      console.log(res.data)
    }).catch(error => {
      console.log(error)
    })

拦截器

在请求或响应被 then 或 catch 处理前拦截它们。

// 添加请求拦截器
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);
  });

跨域处理

在vue-cli项目下的config/index.js中配置跨域处理
在这里插入图片描述

 '/proxy': {
        target: 'http://tingapi.ting.baidu.com', //目标接口域名
        changeOrigin: true,  //是否跨域
        pathRewrite: { //重写接口
          '^/proxy': ''
        }
      }

封装axios中的请求

1.建立一个htttp.js

import axios from 'axios'
import qs from 'qs'
/**
 * 封装post请求
 * @param url 请求的地址
 * @param data 请求的参数(form表单请求)
 * @returns {Promise} 请求返回的结果
 */

export function post (url, data = {}) {
  return new Promise((resolve, reject) => {
    axios({
      method: 'POST',
      header: {'Content-Type': 'application/x-www-form-urlencoded'}, // 设置请求头
      url,
      data: qs.stringify(data)
    }).then(response => {
      resolve(response.data)
    }, err => {
      reject(err)
    })
  })
}

2.在main.js中引入

import {post} from './request/http'
// 定义全局变量
Vue.prototype.$post = post

3.直接使用封装好的请求

  let url = that.globalUrl + 'user/update'
          let data = {
            name: that.registerForm.username, // 用户名
            password: that.registerForm.password // 密码
          }
          that.$post(url, data).then(res => {
            if (res.code === 200) {
              // 跳转到首页
              that.$router.push({path: '/login'})
            } else {
              that.$message.error(res.msg)
            }
          }).catch(err => {
            console.log(err, 'err')
          })

axios操作原生dom

通过给元素添加ref来进行操作原生的dom,然后通过$refs来获取元素

<template>
  <div>
    <p ref="t">23</p>
  </div>
</template>

<script>
export default {
  name: 'baidu',
  created () {
  },
  mounted () {
    let t = this.$refs.t
    // 添加样式
    t.style.color = 'red'
    // 添加事件
    t.addEventListener('click', function () {
      console.log('干啥')
    })
  }
}
</script>

<style scoped>

</style>

使用jquery

切换到项目根目录,执行cnpm install --save jquery 来安装jquery
然后在需要使用的.vue结尾的文件中的script标签中引用

<template>
    <div>
      <p ref="t">哈哈</p>
      <p :class="hello">哈哈</p>
      <ul ref="ul">
        <li>1</li>
        <li>2</li>
      </ul>
    </div>
</template>

<script>
// 引入jquery
import $ from 'jquery'
export default {
  name: 'test',
  data () {
    return {
      hello: 'hello'
    }
  },
  mounted () {
    let t = this.$refs.t
    // 使用jquery
    $(t).css('color', 'red')
    let ul = this.$refs.ul
    $(ul).on('click', 'li', function (event) {
      console.log($(this).html())
    })
  }
}
</script>

<style scoped>
.hello{
  color: deepskyblue;
}
</style>


配置路径

import Vue from 'vue'
import Router from 'vue-router'
import baidu from '../components/baidu'
// 引入组件
import test from '../components/test'
Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'baidu',
      component: baidu
    },
    // 配置路径
    {
      path: '/test',
      name: 'test',
      component: test
    }
  ]
})

页面访问
http://localhost:8080/#/test
在这里插入图片描述

vue中使用echarts

安装命令

cnpm install echarts -S

或者

npm install echarts -S

在vue-cli搭建的vue项目中的main.js中注册echarts

import echarts from 'echarts'
Vue.prototype.echarts = echarts

图表在页面中的宽度和高度

<template>
  <div id="app">
    <!--指定图表的宽度和高度-->
    <div id="v" :style="{width:'300px',height:'600px'}"></div>
  </div>
</template>

初始化折线图

<template>
  <div id="app">
    <!--指定图表的宽度和高度-->
    <div id="v" :style="{width:'300px',height:'600px'}"></div>
  </div>
</template>

<script>
export default {
  name: 'App',
  mounted () {
    this.drawLine()
  },
  methods: {
    drawLine () {
      let myChart = this.echarts.init(document.getElementById('v'))
      // 绘制图表
      myChart.setOption({
        title: {text: '在vue中使用echarts'},
        tooltip: {},
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子']
        },
        yAxis: {},
        series: [{
          name: '销量',
          type: 'bar',
          data: [5, 20, 36, 10]
        }]
      })
      // 图表大小随着浏览器窗口变化而该改变
      window.addEventListener('resize', function () {
        myChart.resize()
      })
    }
  }
}
</script>

<style>
</style>

效果图

在这里插入图片描述

使用Vant

1、先使用vue-cli搭建vue项目
2、使用cmd命令行切换到项目根目录执行以下代码

npm i vant -S

或者使用如下命令

cnpm i vant -S

3、导入所有组件
在vue-cli搭建的项目的src下的main.js中输入以下代码

import Vant from 'vant'
import 'vant/lib/index.css'
Vue.use(Vant)

在这里插入图片描述
4、在页面使用相应的组件

<template>
  <div class="hello">
    <van-button type="primary">按钮</van-button>
  </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>
</style>

5、浏览器访问
在这里插入图片描述

vue-cli使用vant官网介绍

h5 下拉刷新,上拉加载

下拉刷新

加载指定列表分页的第一页的数据

上拉加载

数据需要累加,默认加载第一页的列表分页后的数据,页面滑动到指定的位置,加载下一页的数据,滑动到底部,数据加载完成

使用vue的组件 vue-data-loading

github里vue-data-loading的描述

使用步骤

1、安装命令
使用cmd切换到项目的根目录下执行下列命令

cnpm install vue-data-loading --save

或者

npm install vue-data-loading --save

2、使用
引入组件

import VueDataLoading from 'vue-data-loading'

3、注册组件

components: {VueDataLoading}

4、在需要下拉加载的部分使用vue-data-loading 标签将数据包裹起来.

     <!--列表数据的加载-->
      <vue-data-loading>
      <div class="tableList">
        <div class="tableContent">
          <ul>
            <li v-for="(column,i) in tableDataCompany" :key="i" @click="goDetail(column)">
              <div class="imgCover">
                <el-image :src="column.imgcover" class="img"></el-image>
              </div>
              <div class="itemContent">
                <span style="display: block;font-size: 16px;margin: 10px auto;">{{column.pname}}</span>
                <div class="suitPeople">人群:<span>{{column.suitpeople}}</span></div>
                <div class="suitPeople" style="font-size: 12px">
                                <span style="display: inline-block;vertical-align: middle">
                                  <i class="stageIcon"></i>&nbsp;{{column.stage}}阶段
                                </span>
                  <span style="display: inline-block;vertical-align: middle;margin-left:10px;">
                                  <i class="colleage"></i>&nbsp;{{column.curriculum}}课程
                                </span>
                  <span class="priceShow" style="font-size: 18px;display: inline-block;vertical-align: middle">
                                  <span v-if="column.price == 0" style="color: #F5A724;">免费</span>
                                  <span v-else style="color:red;">¥{{column.price}}</span>
                                </span>
                </div>
              </div>
            </li>
          </ul>
        </div>
      </div>
      </vue-data-loading>

Vuex

官网介绍

共享组件之间的状态(数据),VUe的状态管理模式(可以做到状态共享)

Vuex流程

VueX的流程
在这里插入图片描述

Vuex统一管理状态的好处

  1. 在vuex中集中管理共享的数据,易于开发和后期维护
  2. 高效地实现组件之间地数据共享,提高开发效率
  3. 存储在vuex中地数据都是响应式地,能够实时保持数据与页面的同步

vuex使用场景

组件之间共享的数据,才存储到vuex中,对于组件中的私有数据,依旧存储在组件自身的data中即可

使用步骤

1.安装vuex依赖包
打开项目的根目录,打开cmd命令行输入

cnpm install vuex --save

或者

npm install vuex --save

2、导入vuex包

import Vuex from 'vuex'
Vue.use(Vuex)

3、创建store对象

const store = new Vuex.Store({
  // state中存放的就是全局共享的数据
  state:{
    count:0
  }
})

4、将store对象挂载到vue实例中

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
  router,
  // 将创建的共享数据对象,挂载到vue实例中
  // 所有的组件,就可以直接从store中获取全局的数据了
  store,
  render: h => h(App) // 渲染根组件
}).$mount('#app') // $mount挂载到根元素

State

State提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储

const store =new Vuex.Store({
  state:{count:0}
})
组件访问State中数据

第一种方式:

// count:全局数据名称
this.$store.state.count

第二种方式

//1、从vuex中按需导入mapState函数
import {mapState} from 'vuex'

2、通过刚才导入的mapState函数,将当前组件需要的全局数据,映射为当前组件的computed计算属性

 computed:{
            ...mapState(['count'])
        }

源码

<template>
    <div>
        <h3>当前最新的count值为:{{count}}</h3>
        <button>-1</button>
    </div>
</template>

<script>
import {mapState} from 'vuex'
    export default {
        name: "Substraction",
        computed:{
            ...mapState(['count'])
        }
    }
</script>

<style scoped>

</style>

页面效果
在这里插入图片描述

案例

1、main.js中注册store

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
  router,
  // 将创建的共享数据对象,挂载到vue实例中
  // 所有的组件,就可以直接从store中获取全局的数据了
  store,
  render: h => h(App) // 渲染根组件
}).$mount('#app') // $mount挂载到根元素

2、store 中注入全局变量

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    count:0
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

3、访问全局变量

<template>
    <div>
        <h3>当前最新的count值为:{{$store.state.count}}</h3>
        <button>-1</button>
    </div>
</template>

<script>
    export default {
        name: "Substraction"
    }
</script>

<style scoped>

</style>

4、页面效果
在这里插入图片描述
5、项目格式
在这里插入图片描述

Mutation

Vuex中推荐使用Mutation变更Store中的数据

  1. 只能通过mutation变更Store数据,不可以直接操作Store中的数据
  2. 通过mutatioin虽然复杂,但可以集中监控所有数据的变化

使用

触发mutations的第一种方式

1、定义mutation

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    count:0
  },
  mutations: {
  // state形参代表当前的state
    add(state){
      // 变更状态
      state.count++
    }
  },
  actions: {
  },
  modules: {
  }
})

2、在组件中使用

<template>
    <div>
        <h3>当前最新的count值:{{$store.state.count}}</h3>
        <button @click="btnHandler1">+1</button>
    </div>
</template>

<script>
    export default {
        name: "Adddtion",
        data(){
            return {
            }
        },
        // 触发mutation
        methods:{
            btnHandler1(){
                // 触发mutations的第一种方式
                this.$store.commit('add')
            }
        }
    }
</script>

<style scoped>

</style>

页面效果
在这里插入图片描述

mutation传递参数

可以在触发mutation时传递参数:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    count:0
  },
  mutations: {
  // 只有mutations中定义的函数,才能修改state中的数据
    addCount(state,num){
      // 变更状态
      state.count +=num
    }
  },
  actions: {
  },
  modules: {
  }
})

在组件中调用

<template>
    <div>
        <h3>当前最新的count值:{{$store.state.count}}</h3>
        <button @click="btnHandler1">+1</button>
    </div>
</template>

<script>
    export default {
        name: "Adddtion",
        data(){
            return {
            }
        },
        // 触发mutation
        methods:{
            btnHandler1(){
                // 在调用commit函数
                //触发mutation时携带参数
                this.$store.commit('addCount',5)
            }
        }
    }
</script>

<style scoped>

</style>

页面中点击按钮显示
在这里插入图片描述

触发mutations的第二种方式
// 1.从vuex中按需导入mapMutations函数
import  {mapMutations} from 'vuex'  

通过刚才导入的mapMutations函数,将需要的mutations函数,映射为当前组件的methods方法

 //2.将指定的mutations函数,映射为当前组件的methods函数
            ...mapMutations(['addCount'])

3、在组件中调用

<template>
    <div>
        <h3>当前最新的count值为:{{count}}</h3>
        <button @click="btnHandler2">+1</button>
    </div>
</template>

<script>
    import {mapMutations, mapState} from 'vuex'
    export default {
        name: "Substraction",
        computed:{
            ...mapState(['count'])
        },
        methods:{
            //2.将指定的mutations函数,映射为当前组件的methods函数
            ...mapMutations(['addCount']),
            btnHandler2(){
            // 通过this直接调用store的Mutation中的方法
                this.addCount(12)
            }
        }
    }
</script>

<style scoped>

</style>

页面点击按钮效果
在这里插入图片描述
mutations函数中,不执行异步操作

 import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    count:0
  },
  mutations: {
    // mutations函数中,不执行异步操作,所以异步操作使用action
    add(state){
      setTimeout(()=>{
        state.count ++
      },1000)
    }
  },
  actions: {
  },
  modules: {
  }
})

Action

  • Action提交的是mutation,而不是直接变更状态
  • Action用于处理异步操作,可以包含任意异步操作
  • mutation只能做同步的处理

使用步骤

1、定义action

  actions: {
    // 定义action
    addAsync(context){
    // 在actions中,不能直接修改state中某个数据
      // 必须通过context.commit()触发某个mutation才行
      setTimeout(()=>{
        context.commit('add')
      },1000)
    }
  },

2、在组件中使用

<template>
    <div>
        <h3>当前最新的count值为:{{count}}</h3>
        <button @click="btnHandler2">+1</button>
    </div>
</template>

<script>
    import {mapMutations, mapState} from 'vuex'
    export default {
        name: "Substraction",
        computed:{
            ...mapState(['count'])
        },
        methods:{
            //2.将指定的mutations函数,映射为当前组件的methods函数
            ...mapMutations(['addCount']),
            btnHandler2(){
                this.addCount(12)
            },
            handle(){
                // 触发actions的第一种方式
                // dispatch函数,专门用来触发action
                this.$store.dispatch('addAsync')
            }
        }
    }
</script>

<style scoped>

</style>

源码

<template>
    <div>
        <h3>当前最新的count值为:{{count}}</h3>
        <button @click="btnHandler2">+1</button>
        <button @click="handle">+1 Async</button>
    </div>
</template>

<script>
    import {mapMutations, mapState} from 'vuex'
    export default {
        name: "Substraction",
        computed:{
            ...mapState(['count'])
        },
        methods:{
            //2.将指定的mutations函数,映射为当前组件的methods函数
            ...mapMutations(['addCount']),
            btnHandler2(){
                this.addCount(12)
            },
            handle(){
                // 触发actions的第一种方式
                // dispatch函数,专门用来触发action
                this.$store.dispatch('addAsync')
            }
        }
    }
</script>

<style scoped>

</style>

点击页面+1 Async,count自动加1
在这里插入图片描述

触发actions异步任务时携带参数

1、定义action

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    count:0
  },
  // 只有mutations中定义的函数,才能修改state中的数据
  mutations: {
    addN(state,step){
      state.count+=step
    }
  },
  actions: {
    addNAsync(context,step){
      setTimeout(()=>{
        context.commit('addN',step)
      },1000)
    }
  },
  modules: {
  }
})

2、在组件中调用

<template>
    <div>
        <button @click="handle">+1</button>
        <div>{{count}}</div>
    </div>
</template>

<script>
    import {mapState} from "vuex";
    export default {
        name: "test",
        computed:{
            ...mapState(['count'])
        },
        methods:{
            handle(){
                // 调用dispatch函数
                // 触发actions时携带参数
                this.$store.dispatch('addNAsync',5)
            }
        }
    }
</script>

<style scoped>

</style>


页面效果
在这里插入图片描述
点击按钮之后,页面效果
在这里插入图片描述

action的第二种方式

 this.$store.dispatch()是触发actions的第一种方式,触发actions的第二种方式:

步骤

// 1、从vuex中按需导入mapActions函数
    import {mapActions} from  'vuex';

通过导入的mapActions函数,将需要的actions函数,映射为当前组件的methods方法

  ...mapActions(['addNAsync'])

源码

<template>
    <div>
        <button @click="handle">+1</button>
        <button @click="btnHandle1">异步的+1</button>
        <div>{{count}}</div>
    </div>
</template>

<script>
 // 从vuex中按需导入mapActions函数
 import {mapState, mapActions} from 'vuex'
    export default {
        name: "test",
        computed:{
            ...mapState(['count'])
        },
        methods:{
            ...mapActions(['addNAsync']),
            handle(){
                // 调用dispatch函数
                // 触发actions时携带参数
                this.$store.dispatch('addNAsync',5)
            },
            btnHandle1(){
                this.addNAsync(5)
            }
        }
    }
</script>

<style scoped>

</style>

页面效果
在这里插入图片描述
点击异步的+1按钮,页面效果
在这里插入图片描述
也可以直接调用mapActions的方法
源码如下

<template>
    <div>
        <button @click="handle">+1</button>
        <button @click="addNAsync(5)">异步的+5</button>
        <div>{{count}}</div>
    </div>
</template>

<script>
    import {mapState} from "vuex";
    // 从vuex中按需导入mapActions函数
    import {mapActions} from  'vuex';
    export default {
        name: "test",
        computed:{
            ...mapState(['count'])
        },
        methods:{
            ...mapActions(['addNAsync']),
            handle(){
                // 调用dispatch函数
                // 触发actions时携带参数
                this.$store.dispatch('addNAsync',5)
            }
        }
    }
</script>

<style scoped>

</style>

页面效果
在这里插入图片描述
点击异步的+5按钮之后,页面效果
在这里插入图片描述

Getter

Getter用于对Store中的数据进行加工处理形成新的数据

  1. Getter可以对Store中已有的数据加工处理之后形成新的数据,类似Vue的计算属性
  2. Store中数据发生变化,Getter的数据也会跟着变化
  3. 不修改state中的数据
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    count:0
  },
  // 只有mutations中定义的函数,才能修改state中的数据
  mutations: {
    addCount(state,num){
      // 变更状态
      state.count +=num
    },
    add(state){
      state.count ++
    },
    addN(state,step){
      state.count+=step
    }
  },
  actions: {
    addNAsync(context,step){
      setTimeout(()=>{
        context.commit('addN',step)
      },1000)
    }
  },
  // 定义Getter
  getters:{
    showNum:state=>{
      return state.count
    }
  },
  modules: {
  }
})

使用getters的第一种方式

this.$store.getters.showNum //(showNum:名称)

源码:

<template>
    <div>
        <button @click="handle">+1</button>
        <button @click="addNAsync(5)">异步的+5</button>
        <div>{{count}}</div>
        <!--使用getters-->
        <div>当前最新的值{{$store.getters.showNum}}</div>
    </div>
</template>

<script>
    import {mapState} from "vuex";
    // 从vuex中按需导入mapActions函数
    import {mapActions} from  'vuex';
    export default {
        name: "test",
        data(){
          return {
              num:''
          }
        },
        mounted() {
            this.num = this.$store.getters.showNum
        },
        computed:{
            ...mapState(['count'])
        },
        methods:{
            ...mapActions(['addNAsync']),
            handle(){
                // 调用dispatch函数
                // 触发actions时携带参数
                this.$store.dispatch('addNAsync',5)
            }
        }
    }
</script>

<style scoped>

</style>

页面效果
在这里插入图片描述

使用getters的第二种方式

    import {mapGetters} from 'vuex';
    computed:{
            ...mapGetters(['showNum'])
        }

源码

<template>
    <div>
        <!--使用getters-->
        <div>当前最新的值{{showNum}}</div>
    </div>
</template>

<script>
    import {mapGetters} from 'vuex';
    export default {
        name: "test",
        computed:{
            ...mapGetters(['showNum'])
        }
    }
</script>

<style scoped>

</style>

页面效果
在这里插入图片描述

基于Vuex的案例

  1. 通过vue ui命令打开可视化面板
    在这里插入图片描述
  2. 选择创建项目的位置为桌面
    在这里插入图片描述
    3、点击在此创建新项目按钮,
    在这里插入图片描述
    4、选择手动配置
    在这里插入图片描述
    5、配置路由和vuex
    在这里插入图片描述
    在这里插入图片描述
    6、创建项目
    在这里插入图片描述
    在这里插入图片描述
    7、安装依赖
    在这里插入图片描述
    安装axios
    在这里插入图片描述
    安装ant-design-vue
ant-design-vue官网介绍

在这里插入图片描述
8、在main.js中引入组件库

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 1.导入ant-design-vue组件库
import Antd from 'ant-design-vue'
// 2.导入组件库的样式
import 'ant-design-vue/dist/antd.css'
// 3.安装组件库
Vue.use(Antd)
Vue.config.productionTip = false
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

页面原型

在这里插入图片描述
初始化页面代码


<template>
  <div id="app">
    <a-input placeholder="请输入任务" class="my_ipt" />
    <a-button type="primary">添加事项</a-button>

    <a-list bordered :dataSource="list" class="dt_list">
      <a-list-item slot="renderItem" slot-scope="item">
        <!-- 复选框 -->
        <a-checkbox>{{item.info}}</a-checkbox>
        <!-- 删除链接 -->
        <a slot="actions">删除</a>
      </a-list-item>

      <!-- footer区域 -->
      <div slot="footer" class="footer">
        <!-- 未完成的任务个数 -->
        <span>0条剩余</span>
        <!-- 操作按钮 -->
        <a-button-group>
          <a-button type="primary">全部</a-button>
          <a-button>未完成</a-button>
          <a-button>已完成</a-button>
        </a-button-group>
        <!-- 把已经完成的任务清空 -->
        <a>清除已完成</a>
      </div>
    </a-list>
  </div>
</template>

<script>
export default {
  name: 'app',
  data () {
    return {
      list: [
        {
          id: 0,
          info: 'Racing car sprays burning fuel into crowd.',
          done: false
        },
        { id: 1, info: 'Japanese princess to wed commoner.', done: false },
        {
          id: 2,
          info: 'Australian walks 100km after outback crash.',
          done: false
        },
        { id: 3, info: 'Man charged over missing wedding girl.', done: false },
        { id: 4, info: 'Los Angeles battles huge wildfires.', done: false }
      ]
    }
  }
}
</script>

<style scoped>
  #app {
    padding: 10px;
  }

  .my_ipt {
    width: 500px;
    margin-right: 10px;
  }

  .dt_list {
    width: 500px;
    margin-top: 10px;
  }

  .footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
</style>

初始化渲染数据

清空App.vue中list的数值


<template>
  <div id="app">
    <a-input placeholder="请输入任务" class="my_ipt" />
    <a-button type="primary">添加事项</a-button>

    <a-list bordered :dataSource="list" class="dt_list">
      <a-list-item slot="renderItem" slot-scope="item">
        <!-- 复选框 -->
        <a-checkbox>{{item.info}}</a-checkbox>
        <!-- 删除链接 -->
        <a slot="actions">删除</a>
      </a-list-item>

      <!-- footer区域 -->
      <div slot="footer" class="footer">
        <!-- 未完成的任务个数 -->
        <span>0条剩余</span>
        <!-- 操作按钮 -->
        <a-button-group>
          <a-button type="primary">全部</a-button>
          <a-button>未完成</a-button>
          <a-button>已完成</a-button>
        </a-button-group>
        <!-- 把已经完成的任务清空 -->
        <a>清除已完成</a>
      </div>
    </a-list>
  </div>
</template>

<script>
export default {
  name: 'app',
  data () {
    return {
      list: [
      ]
    }
  }
}
</script>

<style scoped>
  #app {
    padding: 10px;
  }

  .my_ipt {
    width: 500px;
    margin-right: 10px;
  }

  .dt_list {
    width: 500px;
    margin-top: 10px;
  }

  .footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
</style>

显示如下:
在这里插入图片描述

1、在public文件夹下新建个文件为list.json

[
  {
    "id": 0,
    "info": "Racing car sprays burning fuel into crowd.",
    "done": true
  },
  { "id": 1, "info": "Japanese princess to wed commoner.", "done": false },
  {
    "id": 2,
    "info": "Australian walks 100km after outback crash.",
    "done": true
  },
  { "id": 3, "info": "Man charged over missing wedding girl.", "done": false },
  { "id": 4, "info": "Los Angeles battles huge wildfires.", "done": false }
]

在这里插入图片描述
2、在store/index.js中引入axios加载数据

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    // 所有的任务列表
    list: []
  },
  mutations: {
    // 初始化数据
    initList (state, list) {
      state.list = list
    }
  },
  actions: {
    // 异步操作
    // 获取列表数据
    getList (context) {
      axios.get('/list.json').then(({ data }) => {
        console.log(data)
        context.commit('initList', data)
      })
    }
  },
  modules: {
  }
})


在这里插入图片描述

3、在App.vue中加载列表数据


<template>
  <div id="app">
    <a-input placeholder="请输入任务" class="my_ipt" />
    <a-button type="primary">添加事项</a-button>

    <a-list bordered :dataSource="list" class="dt_list">
      <a-list-item slot="renderItem" slot-scope="item">
        <!-- 复选框 -->
        <a-checkbox>{{item.info}}</a-checkbox>
        <!-- 删除链接 -->
        <a slot="actions">删除</a>
      </a-list-item>

      <!-- footer区域 -->
      <div slot="footer" class="footer">
        <!-- 未完成的任务个数 -->
        <span>0条剩余</span>
        <!-- 操作按钮 -->
        <a-button-group>
          <a-button type="primary">全部</a-button>
          <a-button>未完成</a-button>
          <a-button>已完成</a-button>
        </a-button-group>
        <!-- 把已经完成的任务清空 -->
        <a>清除已完成</a>
      </div>
    </a-list>
  </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
  name: 'app',
  data () {
    return {
    }
  },
  computed: {
    // ... 对象展开运算符引入state中的数据
    ...mapState(['list'])
  },
  created () {
    // 触发actions
    this.$store.dispatch('getList')
  }
}
</script>

<style scoped>
  #app {
    padding: 10px;
  }

  .my_ipt {
    width: 500px;
    margin-right: 10px;
  }

  .dt_list {
    width: 500px;
    margin-top: 10px;
  }

  .footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
</style>


4、页面样式展示以及控制台输出
在这里插入图片描述

完整代码

App.vue

<template>
  <div id="app">
    <a-input placeholder="请输入任务" class="my_ipt" :value="inputValue" @change="handleInputChange" />
    <a-button type="primary" @click="addItemToList">添加事项</a-button>

    <a-list bordered :dataSource="infolist" class="dt_list">
      <a-list-item slot="renderItem" slot-scope="item">
        <!-- 复选框,箭头函数:=> -->
        <a-checkbox :checked="item.done" @change="(e) =>{
          cbStatusChanged(e,item.id)
        }">{{item.info}}</a-checkbox>
        <!--也可以用如下写法-->
         <!-- <a-checkbox :checked="item.done" @change="changeStatus($event,item)">{{item.info}}</a-checkbox>-->
        <!-- 删除链接 -->
        <a slot="actions" @click="removeItemById(item.id)">删除</a>
      </a-list-item>

      <!-- footer区域 -->
      <div slot="footer" class="footer">
        <!-- 未完成的任务个数 -->
        <span>{{unDoneLength}}条剩余</span>
        <!-- 操作按钮 -->
        <a-button-group>
          <a-button :type="viewKey==='all'?'primary':'default'" @click="changeList('all')">全部</a-button>
          <a-button :type="viewKey==='undone'?'primary':'default'" @click="changeList('undone')">未完成</a-button>
          <a-button :type="viewKey==='done'?'primary':'default'" @click="changeList('done')">已完成</a-button>
        </a-button-group>
        <!-- 把已经完成的任务清空 -->
        <a @click="clean">清除已完成</a>
      </div>
    </a-list>
  </div>
</template>

<script>
import { mapState, mapGetters } from 'vuex'
export default {
  name: 'app',
  data () {
    return {
    }
  },
  computed: {
    // ... 对象展开运算符引入state中的数据
    // ...mapState(['list', 'inputValue', 'viewKey']),
    ...mapState(['inputValue', 'viewKey']),
    ...mapGetters(['unDoneLength', 'infolist'])
  },
  created () {
    // 触发actions
    this.$store.dispatch('getList')
  },
  methods: {
    // 修改页面上展示的列表的数据
    changeList (key) {
      this.$store.commit('changeViewKey', key)
    },
    // 清除已完成的任务
    clean () {
      this.$store.commit('cleanDone')
    },
    // 监听复选框选中状态变化事件
    cbStatusChanged (e, id) {
      // 通过e.target.checked可以接受到最新的选中状态
      const param = {
        id: id,
        status: e.target.checked
      }
      this.$store.commit('changeStatus', param)
    },
    // 监听文本框内容变化
    handleInputChange (e) {
      this.$store.commit('setInputValue', e.target.value)
    },
    // 向列表中新增 item 项
    addItemToList () {
      if (this.inputValue.trim().length <= 0) {
        return this.$message.warning('文本框内容不能为空!')
      }
      this.$store.commit('addItem')
    },
    // 根据Id删除对应的任务事项
    removeItemById (id) {
      this.$store.commit('removeItem', id)
    }
  }
}
</script>

<style scoped>
  #app {
    padding: 10px;
  }

  .my_ipt {
    width: 500px;
    margin-right: 10px;
  }

  .dt_list {
    width: 500px;
    margin-top: 10px;
  }

  .footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
</style>

store.js

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    // 所有的任务列表
    list: [],
    // 文本输入框中的值
    inputValue: 'AAA',
    // 下一个Id
    nextId: 5,
    viewKey: 'all' // 视图的key值
  },
  mutations: {
    // 初始化数据
    initList (state, list) {
      state.list = list
    },
    // 为 store 中的 inputValue 赋值
    setInputValue (state, val) {
      state.inputValue = val
    },
    // 添加列表项
    addItem (state) {
      const obj = {
        id: state.nextId,
        info: state.inputValue.trim(),
        done: false
      }
      state.list.push(obj)
      state.nextId++
      state.inputValue = ''
    },
    removeItem (state, id) {
      // 根据id查找对应的索引
      const index = state.list.findIndex(x => x.id === id)
      // 根据索引,删除对应的元素
      if (index !== -1) {
        state.list.splice(index, 1)
      }
    },
    // 修改列表项的选中状态
    changeStatus (state, param) {
      const i = state.list.findIndex(x => x.id === param.id)
      if (i !== -1) {
        state.list[i].done = param.status
      }
    },
    // 清除已完成的任务
    cleanDone (state) {
      state.list = state.list.filter(x => x.done === false)
    },
    // 修改视图的key
    changeViewKey (state, key) {
      state.viewKey = key
    }
  },
  actions: {
    // 异步操作
    // 获取列表数据
    getList (context) {
      axios.get('/list.json').then(({ data }) => {
        console.log(data)
        context.commit('initList', data)
      })
    }
  },
  getters: {
    // 统计未完成的任务条数
    unDoneLength (state) {
      return state.list.filter(x => x.done === false).length
    },
    // 获取list数据,根据视图主键过滤
    infolist (state) {
      if (state.viewKey === 'all') {
        return state.list
      }
      if (state.viewKey === 'undone') {
        return state.list.filter(x => !x.done)
      }
      if (state.viewKey === 'done') {
        return state.list.filter(x => x.done)
      }
      return state.list
    }
  },
  modules: {
  }
})

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值