vue-vue2学习笔记

1. 安装

全局安装webpack,全局安装Vue脚手架

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

cnpm install webpack -g
cnpm install vue-cli -g

2. 创建项目

// 2.0 vue-cli
vue init webpack [name]
npm run dev
// 3.0 vue-cli
vue create [name]
npm run serve
// 在终端打开命令
vue ui

3. v-html 数据绑定

模板语法:{{ }}

v-html:可以解析标签

v-text:解析为纯文本

new Vue({
  el: '#app',
  data: {
    name: 'Vue',
    isBool: true
  }
})
<h2>模板语法</h2>
<h4>{{name}}</h4>
<h4>hello,{{name}}</h4>
<h4>{{'hello,'+name}}</h4>

<!-- v-html -->
<h2>v-html</h2>
<h4 v-html="'hello,'+name"></h4>
<h4 v-html="isBool?'success':'error'"></h4>
<h4 v-html="Math.random()"></h4><br>

<!-- v-text -->
<h2>v-text</h2>
<h4 v-text="'hello,'+name"></h4>
<h4 v-text="isBool?'success':'error'"></h4>
<h4 v-text="Math.random()"></h4><br>

4. v-bind 属性绑定

v-bind 绑定属性,可以写成 " : "(冒号)

new Vue({
  el: '#app',
  data: {
    link: './img/bg01.jfif',
    web: {
      link: './img/bg02.jfif',
      src: './2-数据绑定.html',
      title: '数据绑定'
    }
  }
})
  <div id="app">
    <img v-bind:src="link" alt=""><br>
    <a v-bind:href="web.src">
      <img v-bind:src="web.link" alt="" v-bind:title="web.title">
    </a>
  </div>

5. v-model 表单数据绑定

  1. 输入框直接 v-model 获取输入内容
  2. 单选框获取 value 的值
  3. 下拉菜单:select 绑定v-model,获取 option 的value值
  4. 文本域直接获取 v-model 的值
  5. 多选框:获取 value 的多个值,用数组接收
// 实例化Vue
const vue = new Vue({
  el: '#app',
  data: {
    user: {
      name: "",
      pass: "",
      sex: "",
      job: "",
      mess: "",
      hobby: [],
      agree: false
    },
  },
  methods: {
    submit: function() {
      for (var key in this.user) {
        console.log(this.user[key])
      }
    }
  },
})
  <div id="app">
    <!-- 输入框 -->
    <div>账号:<input type="text" name="name" v-model="user.name"></div>
    <div>密码:<input type="text" name="pass" v-model="user.pass"></div>
    <!-- 单选框 -->
    <div>性别:
      <input type="radio" name="sex" v-model="user.sex" value="0-男"><input type="radio" name="sex" v-model="user.sex" value="1-女"></div>
    <!-- 下拉框 -->
    <div>职业:
      <select name="job" v-model="user.job">
        <option value="">--选择--</option>
        <option value="web">web工程师</option>
        <option value="python">python工程师</option>
        <option value="C++">C++工程师</option>
      </select>
    </div>
    <!-- 文本域 -->
    <div>留言:
      <textarea name="mess" v-model="user.mess" id="" cols="30" rows="10"></textarea>
    </div>
    <!-- 复选框 -->
    <div>爱好:
      <input type="checkbox" name="book" v-model="user.hobby" value="book">读书
      <input type="checkbox" name="game" v-model="user.hobby" value="game">打游戏
      <input type="checkbox" name="code" v-model="user.hobby" value="code">敲代码
    </div>
    <!-- 同意 -->
    <div>
      <input type="checkbox" v-model="user.agree" value="agree" name="" id="">同意
    </div>
    <!-- 提交按钮 -->
    <button v-on:click="submit">提交</button>
  </div>

6. v-if 条件渲染

  1. v-if:当条件为真时渲染,否则渲染 v-else不渲染时删除节点
  2. v-show:不渲染时 display: none;
const vue = new Vue({
  el: '#app',
  data: {
    isShow: true
  }
})
  <div id="app">
    <!-- v-if -->
    <p v-if="isShow">hello</p>
    <P v-else>world</P>
    <!-- v-show -->
    <p v-show="isShow">hello</p>
  </div>

7. v-for 列表渲染

// 实例化Vue
const vue = new Vue({
  el: '#app',
  data: {
    imgArr: [
      "./img/bg01.jfif",
      "./img/bg02.jfif",
      "./img/bg03.jfif",
      "./img/bg04.jfif",
      "./img/bg05.jfif",
      "./img/bg06.jfif",
      "./img/bg07.jfif",
      "./img/bg08.jfif"
    ]
  }
})
<div id="app">
  <img v-for="item in imgArr" v-bind:src="item" alt="">
</div>

8. 动态样式

.red{
    background: red;
}
.blue{
    background: blue;
}
const vue = new Vue({
  el: '#app',
  data: {
    isShow: true
  }
})
  <div id="app">
    <p v-bind:class="[isShow ? 'red' : 'blue']">hello</p>
    <P v-bind:class="{'red':true, 'blue':false}">world</P>
  </div>

9. v-on - 事件绑定

1. 基本使用

v-on 事件绑定,可以写成 “ @ ”

const vue = new Vue({
  el: '#app',
  data: {
    num: 10
  },
  methods: {
    sub: function () {
      this.num--;
    },
    add() {
      this.num++;
    }
  }
})
  <div id="app">
    <h1>计数器</h1>
    <h2>num:{{num}}</h2>
    <button v-on:click="sub">--</button>
    <button @click="add">++</button>
  </div>
2. 事件修饰符

.prevent:阻止默认事件

.stop:阻止事件冒泡

.capture:事件捕获

.once:一次性事件

.self:只触发本事事件

.native:自定义组件绑定事件时 (在 router-link 上绑定事件时使用)

<div class="box" v-on:contextmenu.prevent="click('取消默认事件')">默认事件</div>
<div class="box" v-on:click.stop="click('阻止事件冒泡')">事件冒泡</div>
<div class="box" v-on:click.capture="click('事件捕获')">事件捕获</div>
<div class="box" v-on:click.once="click('一次性事件')">一次性事件</div>
<div class="box" v-on:click.self="click('事件本事')">事件本事</div>

@scroll.passive:滚动事件

<div @scroll.passive="scroll">...</div>

@click.left:鼠标左键

@click.right:鼠标右键

@click.middle:鼠标中键

<button @click.left>鼠标左键按下</button>
<button @click.right>鼠标右键按下</button>
<button @click.middle>鼠标中键按下</button>

.enter:回车键按下

.top .down .left .right

<input type="text" @keydown.enter="keydown('回车键按下')">
<input type="text" @keydown.up="keydown('up键按下')">
<input type="text" @keydown.down="keydown('down键按下')">
<input type="text" @keydown.left="keydown('left键按下')">
<input type="text" @keydown.right="keydown('right键按下')">
3. 表单修饰符

.lazy:表单失去焦点时响应

.trim:表单输入内容有空格时,失去焦点自动清除空格

<input type="text" v-model.lazy="name">
<input type="text" v-model.trim="name">

10. JSONP

  1. 创建 <script> 标签

  2. <script> 添加 src 属性

  3. 插入到页面

    var url = "接口地址"
    var os = document.createElement('script');
    os.src = url;
    document.body.appendChild(os);
    

11. watch 监听属性

当监听的属性发生改变时,触发 watch 里的函数

<div id="app">
  <input type="text" v-model="name" />
</div>

监听的属性不需要this,使用function函数

new Vue({
    el: '#app',
    data: {
        name: ""
    },
    watch: {
        name: function(){
            console.log("namg改变了");
        }
    }
})

基本数据类型 简单监听

复合数据类型 深度监听

watch: {
    name: {
        handler(newd, oldd){
            console.log("深度监听");
        },
        immediate: true,    // 立即监听
        deep: true  // 是否深度监听,默认false
    }
}

12. computed 计算属性

计算属性不能初始化,根据其他属性计算得到

计算属性已函数的形式定义

new Vue({
    el: '#app',
    data: {
        arr: [1,2,3,4,5]
    },
    computed:{
        // 获取
        resArr(){
            return arr.map(val=>{
                return val*10;
            })
        },
        // 获取和设置
        title: {
            get: function(){
                return this.arr[0] + "-title"
            },
            set: function(str){
                this.arr[1] = "001" + str
            }
        }
    }
})
<div id="app">
  <ul>
    <li v-for="item in resArr">{{item}}</li>
  </ul>
</div>

13. 服务器返回数据的问题

服务器返回的数据中,当修改了某个属性时,可能不会引起页面的自动渲染,解决办法:

1. 取出数据
2. 修改数据
3. 重新赋值

14. filter 过滤器

// 实例化Vue
const vue = new Vue({
  el: '#app',
  data: {
    arr: [
      {id:1, name:"html", price:180, num:100},
      {id:2, name:"vue", price:200, num:50},
      {id:3, name:"node", price:240, num:10},
    ],
  },
  methods: {},
  watch: {},
  computed: {},
  // 局部定义过滤器
  filters: {
    // 每本原价减 20
    filPrice(val) {
      return val-20;
    }
  }
})
  <div id="app">
    <h1>局部filter</h1>
    <ul>
      <li>
        <h4>名字</h4>
        <h4>原价</h4>
        <h4>现价(filter)</h4>
        <h4>数量</h4>
      </li>
      <li v-for="item in arr" :key="item.id">
       <span>《{{item.name}}》</span>
       <span><del>{{item.price}}</del></span>
       <span>{{item.price | filPrice}}元</span>
       <span>{{item.num}}本</span>
      </li>
    </ul>
  </div>

全局过滤器

filter / index.js

const timerFil = (val)=>{
  let date = new Date(val);
  let year = date.getFullYear(); // 年
  let month = (date.getMonth()+'').padStart(2,'0'); // 月
  let day = (date.getDate()+'').padStart(2,'0'); // 日
  let hour = (date.getHours()+'').padStart(2,'0'); // 时
  let min = (date.getMinutes()+'').padStart(2,'0'); // 分
  let second = (date.getSeconds()+'').padStart(2,'0'); // 秒

  return `${year}-${month}-${day} ${hour}:${min}:${second}`;
}

export default{
  timerFil
}

main.js

import filter from './filter/index'
// 注册全局过滤器
for(let i in filter){
  Vue.filter(i, filter[i])
}

index.vue

<p>时间:{{timer | timerFil}}</p>

15. <transition> 动画

<style> 在 module 模式下无效

<!-- appear:进场动画不生效时使用 -->
<transition name="fade" appear>
    <div v-if="show">
        <h4 class="message_title">{{ title }}</h4>
    </div>
</transition>
/* 动画必须写在样式下面,否则不生效 */
/*---------- 动画 ----------*/
/* 动画开始 */
.flash-enter,.flash-leave-to{
  width: 0px;
  height: 0px;
}
/* 运行时间 */
.flash-enter-active,.flash-leave-active{
  transition: all 600ms;
}
/* 动画结束 */
.flash-enter-to,.flash-leave{
  width: 100px;
  height: 100px;
}

15.1 <transition> Vue3

**使用 v-if **

<style> 在 module 模式下无效

<!-- appear:进场动画不生效时使用 -->
<transition name="fade" appear>
    <div v-if="show" style="top: -16px; left: -48px;">
        <h4 class="message_title">{{ title }}</h4>
    </div>
</transition>
/* 动画必须写在样式下面,否则不生效 */
/* 动画开始 */
.icon-msg-enter-from,
.icon-msg-leave-to{ top: 0; left: 0; }
/* 运行 */
.icon-msg-enter-active,
.icon-msg-leave-active{ transition: all 600ms linear; }
/* 动画结束 */
/* 标签上加了样式后可以不写结束样式 */
.icon-msg-enter-to,
.icon-msg-leave-from{ top: -16px; left: -48px; }

动画库:animate.css

  <div id="app">
    <transition 
      enter-active-class="animate [动画名称]"
      leave-active-class="animate [动画名称]">

      <div class="box" v-show="isShow"></div>
    </transition>
  </div>

16. *****生命周期*****

const vue=new Vue({
  el: '#app',
  data: {
    num: "",
  },
  methods: {},
  watch: {},
  computed: {},
  /*
      创建实例开始
      实例初始化之后,数据(data)不可用
  */
  beforeCreate() {
    console.log("--创建实例开始--");
    console.log("el:",this.$el);
    console.log("data:",this.$data);
  },
  /*
      创建实例完成
      实例创建完成,数据(data)可以使用,$el未挂载,不可用
  */
  created() {
    console.log("--创建实例完成--");
    console.log("el:",this.$el);
    console.log("data:",this.$data);
  },
  /*
      挂载节点开始
  */
  beforeMount(){
    console.log("--挂载节点开始--");
    console.log("el:",this.$el);
    console.log("data:",this.$data);
  },
  // 挂载节点完成 (常用)
  mounted(){
    console.log("--挂载节点完成--");
    console.log("el:",this.$el);
    console.log("data:",this.$data);
  },
  // 更新节点开始
  beforeUpdate(){
    console.log("--更新节点开始--");
    console.log("el:",this.$el);
    console.log("data:",this.$data);
  },
  // 更新节点完成
  updated() {
    console.log("--更新节点完成--");
    console.log("el:",this.$el);
    console.log("data:",this.$data);
  },
  // 销毁实例前 (常用)
  beforeDestroy(){
    console.log("--销毁实例--");
    console.log("el:",this.$el);
    console.log("data:",this.$data);
  },
  // 销毁实例完成
  destroyed(){
    console.log("--销毁实例完成--");
    console.log("el:",this.$el);
    console.log("data:",this.$data);
  }
})

在使用 <keep-alive> 时,组件将被缓存,被缓存的组件无法触发 mounted 生命周期

// 缓存的组件激活时
activated(){
    console.log("缓存组件开始使用");
}
// 缓存组件停用时
deactivated(){
    console.log("缓存组件结束使用");
}
Vue 实例生命周期

17. ref 获取节点

<div id="app">
    <p ref="text">hello world</p>
</div>
const vue=new Vue({
  el: '#app',
  data: {
    num: "",
  },
  methods: {},
  watch: {},
  computed: {},
  // 挂载节点完成
  mounted(){
    var result = this.$refs.text.innerHTML;
    console.log(result);
  },
})

17-1. ref使用

ref不能和 v-if 一起使用,v-show可以

18. vue-cli 脚手架

项目目录

-src
    -assets //静态资源
        -css
            reset.css
        -js
            rem.js
    -pages  //路由组件
        login.vue
        index.vue
    -common  //公共组件
    -components  //页面组件
    -filter  //过滤器
        index.js
    -router  //路由
        index.js
    -util    //工具目录
        request.js    //数据请求
    App.vue
    main.js

19. $isServer 是否运行在服务器上

// 当前 Vue 实例是否运行于服务器
created(){
    this.$isServer;
}
// 挂载实例 (到 id=app)
new Vue({}).$mount('#app');

20. slot 插槽

**单插槽:**使用<slot>标签实现插槽

<!-- v-main组件 -->
<template>
    <h1>单插槽</h1>
    <slot></slot>
</template>
<template>
    <v-main>
        <p>这里是插槽内容</p>
    </v-main>
</template>

**多插槽:**通过给<slot>标签添加name属性,定义多个插槽

<!-- v-main组件 -->
<template>
    <h2>one</h2>
    <slot name="one"></slot>
    <h2>two</h2>
    <slot name="two"></slot>
</template>
<template>
    <v-main>
        <p slot="one">这里是插槽 1 内容</p>
        <p slot="two">这里是插槽 2 内容</p>
    </v-main>
</template>

Vue3 插槽

<div>
    <slot></slot>
</div>
<!-- 插槽默认名称default -->
<template v-slot:default>
    <span>123</span>
</template>

插槽传值

<div>
    <slot value="123"></slot>
</div>
<!-- 插槽默认名称default -->
<template v-slot:default="value">
    <span>value</span>
</template>

21. 父子组件通信

父传子或者子传父都是父组件定义,子组件接收

1. 父 --> 子
  1. 通过给子组件绑定一个自定义属性,属性绑定一个值

  2. 子组件通过 props 数组接收

<!-- 父组件 -->
<template>
    <h2>父组件</h2>
    <v-child v-bind:msg="message"></v-child>
</template>
<script>
export default{
    data(){
        return {
            message: "hello Vue"
        }
    }
}
</script>
<!-- 子组件 -->
<template>
    <h2>子组件</h2>
    <p>{{ msg }}</p>
</template>
<script>
export default{
    props: {
        msg: {
            type: String,
            default: "hello Vue",
            required: true, // 必传
        }
    }
}
</script>
2. 子 --> 父
  1. 子组件 定义一个自定义事件
  2. 通过 this.$emit绑定
  3. 父组件 接收自定义事件
<!-- 子组件 -->
<template>
    <h2>子组件</h2>
    <button v-on:click="cancel">自定义事件</button>
</template>
<script>
export default{
    methods:{
        cancel(){
            this.$emit('cancel')
        }
    }
}
</script>
<!-- 父组件 -->
<template>
    <h2>父组件</h2>
    <v-child v-on:cancel="cancel"></v-child>
    <p v-show="isShow">hello Vue</p>
</template>
<script>
export default{
    data(){
        return {
            isShow: false
        }
    }
    methods:{
        cancel(){
            this.isShow = !this.isShow;
            console.log("子组件点击了");
        }
    }
}
</script>

22. vue-router 路由

1. 安装
npm install vue-router --save
2. 路由规则

当一级路由有二级路由时,一级路由不加name属性

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)

import index from '../pages/index.vue'

export default new Router({
  routes: [
    // 首页
    {
      path: '/index',
      component: index,
    },

    // 重定向
    {
      path: '*',
      component: index
    }
  ]
})

vue3.0 + vue-router

npm install vue-router@4 -D
import { createRouter, createWebHistory } from 'vue-router'
const routerHistory = createWebHistory()

const index = ()=>import('../components/index.vue')

const router = createRouter({
    history: routerHistory,
    routes: [
      {
        path: '/',
        component: index
      },
      {
        path: '*',
        component: index
      }
    ]
})

export default router
3. 路由出口

App.vue

<template>
  <div>
    <!-- 路由出口 -->
    <router-view></router-view>
  </div>
</template>

index.vue

<template>
  <div>
    <h1>这里是首页</h1>
    <!-- 二级路由出口 -->
    <router-view></router-view>
  </div>
</template>
4. 路由跳转
<router-link to="/index" active-class="active">首页</router-link>
<!-- replace:router.replace() -->
<router-link :to="/index" replace></router-link>
this.$router.push('/index')
this.$router.replace('/index')
this.$router.go(-1)

通过 name 属性跳转路由

this.$router.push({
    name: 'index',
    params: {
        id: "001"
    }
})
// 重复进入同一个路由报错解决方案
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
    return originalPush.call(this, location).catch(err => err);
};
5. 路由传参

**$route:**路由信息

**$router:**路由使用

方式一 : (query)

query传递参数不会出现路参数丢失的情况,所以不需要做其他的配置,不过缺点就是参数会拼接在url后面: url?xx=yy 这种方式来传递参数,会暴露参数,并且url也有字符上限限制

<!-- 传参组件 -->
<router-link v-bind:to="'/index?id='+id">首页</router-link>
this.$router.push({path: '/index', query: {id:1}})
<!-- 收参组件 -->
<script>
export default{
    mounted(){
        this.$route.query.id
    }
}
</script>

方式二: (params)

params传递参数是将参数放在route对象中,没有放在url后面,但是会有一个问题,在跳转之后的页面中刷新的时候,会导致当前路由中保存的params的参数丢失

export default new Router({
  routes: [
    // 首页
    {
      path: '/index:id',
      component: index,
    }
  ]
})
<!-- 传参组件 -->
<router-link v-bind:to="'/index/'+id">首页</router-link>
this.$router.push({name: 'index', params:{id:1}})
<!-- 收参组件 -->
<script>
export default{
    mounted(){
        this.$route.params.id
    }
}
</script>

方式三 params

// 传参组件
this.$router.push({
    name:'detail',
    params:{
        id: "001"
    }
})
// 接参组件
mounted() {
    var id = this.$route.params.id;
    console.log(id);
}

router.back() 传参

// 传参的页面
this.$route.query.id = '123456';
// 接参的页面
beforeRouteEnter(to, from, next) {
        next(vm => {
            if (from.name == [上一个页面]") {
        // vm指向this实例
                vm.id= from.query.id;
            }
        });
    },
6. 登录拦截
// 进入页面时拦截 (全局)
router.beforeEach((to, from, next)=>{
    if (to.path==='/login') {
        next()
        return;
    }
    next('/login')
})
// 离开页面时拦截 (全局)
router.afterEach((route)=>{
    console.log(route);
})
// 指定页面进入的时候 (路由)
export default new Router({
  routes: [
    {
      path: '/index',
      component: index,
      // beforeEnter
      beforeEnter: (to, from, next) => {
        next()
      }
  ]
})
<!-- 页面内 (内部) -->
<script>
export default {
  props: [],
  data() {
    return {}
  },
  // beforeRouteEnter(){ }
  beforeRouteEnter (to, from, next) {
    next()
  },
  // beforeRouteLeave(){ }
  beforeRouteLeave (to, from, next) {
    next()
  },
  components: {},
  mounted() {}
}
</script>
7. 加载方式
// hash 模式
export default new Router({
  mode: "history",
  routes: [
    {
      path: '/index',
      component: index,
      // beforeEnter
      beforeEnter: (to, from, next) => {
        next()
      }
  ]
})
8. 重定向
{
    path: '/datastatist',
    name: 'datastatist',
    component: () => import('../views/dataStatistics'),
    // redirect 重定向
    redirect: '/datastatist/index',
    children: [
        {
            path: '/datastatist/index',
            name: 'datastatist/index',
            component: () => import('../views/dataStatistics/profile-data.vue'),
        },
    ],
},

Vue3 路由重定向

const routes = [
    {
        path: "/",
        name: "index",
        component: () => import("/@/views/main/index.vue"),
    },
    {
        path: "/:pathMatch(.*)*",
        redirect: { name: "index" }
    }
];

23. 懒加载

import index from './index.vue'
const index = ()=>import('./index.vue')

24. axios 数据请求

1. 安装 axios
npm install axios --save

// 请求头设置
axios.defaults.baseURL = "接口地址";
2. 配置代理

config / index.js

proxyTable: {
  "/api": {
    target: "http://localhost:3000",
    changeOrigin: true,
    secure: false,  // 如果是https接口,需要配置这个参数
    pathRewrite: {
      "^/api": "http://localhost:3000"
    }
  }
}
3. 数据请求

util / request.js

  1. **GET:**params: { }
  2. **POST:**data: { }
  3. **PUT:**data:{ }
import axios from 'axios'

let url = '/api'

// 登录请求
export const loginRequest = (data) => {
  return axios({
    method: 'post',
    url: url+'/login',
    headers: {    //设置请求头(token)
      'token':'xxxxxx'
    },
    data: data
  })
}

pages / login.vue

<script>
// loginRequest --> 引入登录请求
import { loginRequest } from "../util/ajax";
export default {
  methods: {
    login() {
      loginRequest(this.user).then((message)=>{
        if (message.data.ok) {
          this.$router.push('/index')
        } else {
          alert(message.data.message)
        }
      })
    }
  }
</script>
3.1 axios.all

同时请求多个请求

const axios1 = (data) => {
  return axios({
    method: 'post',
    url: url+'/login',
    data: data
  })
}
const axios2 = (data) => {
  return axios({
    method: 'get',
    url: url+'/index',
    data: data
  })
}
// 同时请求两个请求
this.$axios.all([axios1, axios2])
    .then(this.$axios.spread((item1, item2) => {
        console.log(item1);
        console.log(item2);
    }
))
4. 请求拦截 响应拦截
import axios from 'axios'

// 请求拦截
axios.interceptors.request.use((req)=>{
      return req;
})

// 响应拦截
axios.interceptors.response.use((res)=>{
  console.log("请求地址:"+res.config.url);
  console.log(res);
  return res;
})

let url = '/api'
// 登录请求
export const loginRequest = (data) => {
  return axios({
    method: 'post',
    url: url+'/login',
    data: data
  })
}
5. 上传文件

手动上传文件 + 获取上传进度

httpRequest(file){
    let formdata = new FormData();
    formdata.append("file", file.file);
    for(let item in this.option){
        formdata.append(item, this.option[item]);
    }
    axios({
        url: "http://localhost:6061/upload",
        method: "post",
        data: formdata,
        onUploadProgress: (progress) => {
            let num = progress.loaded / progress.total * 100 | 0; 
            this.uploadlist[this.uploadlist.length - 1].progress = num;
        }
    }).then((res) => {
        console.log(res);
    });
},

25. Vuex 状态管理

  1. 引入vuex
  2. 定义状态
  3. main引入,注册
  4. 组件使用
0. 引入
npm install vuex --save
1. 状态
import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)

// 状态
const state = {
  name: "vue"
}

// 修改状态:函数
const mutations = {
  changeName(state, name) {
    state.name = name;
  }
}

// 动作:修改状态 (有参数时要写上参数)
const actions = {
  setName(context, name) {
    context.commit('changeName', name);
  }
}

// 计算属性
const getters = {
  getName(state) {
    return state.name
  }
}

export default new vuex.Store({
  // 状态
  state,
  // 修改状态:函数
  mutations,
  // 动作:修改状态
  actions,
  // 计算属性
  getters
})
2. main中注册
import store from './store/index';

new Vue({
  el: '#app',
  router,
  store
})
3. 简单使用
<template>
  <div>
    <h1>vuex-one</h1>
    <p>{{$store.state.name}}</p>
    <p class="text" v-if="$store.state.ste">{{$store.state.ste}}</p>
    <p class="text active" v-else>{{$store.state.ste}}</p>
    <button v-on:click="$store.dispatch('setName', 'javascript')">名字</button>
    <button v-on:click="$store.dispatch('setSte')">状态</button>
  </div>
</template>
4. 统一导出
<template>
  <div>
    <h1>vuex-two</h1>
    <p>{{getName}}</p>
    <p class="text">{{getSte}}</p>
    <button v-on:click="setName('javascript')">名字</button>
    <button v-on:click="setSte">状态</button>
  </div>
</template>
<script>
// 从vuex中导入方法
import { mapGetters, mapActions } from "vuex";
export default {
  // 读取仓库里的方法
  methods: {
    ...mapActions(["setName","setSte"])
  },
  // 读取仓库里的状态
  computed: {
    ...mapGetters(["getName", "getSte"])
  },
};
</script>
5. vuex模块化管理

store / index.js

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

// 状态
const state = {
  number: 1
};

// 计算属性
const getters = {
  number(){ return state.number }
};

// 修改状态
import {mutations} from './mutations'
import {actions} from './actions'

// 状态管理模块
import login from './modules/login'

// 实例化 vuex
export default new vuex.Store({
  state,
  mutations,
  actions,
  getters,
  modules:{
    login
  }
})

store / actions.js

/*
          动作:状态改变
  1. 调用 mutations 文件中封装的函数
  2. commit 一下 mutations 中的函数名即可
  3. 有参数时加入参数
  4. context 固定参数,指向 vuex 本身
  5. 组件中使用时使用这里的函数
*/
export const actions = {
  add(context) {
    context.commit("addNumber");
  },
  sub(context) {
    context.commit("subNumber");
  }
};

store / mutatons.js

/*
        修改状态
  1. 函数:写逻辑函数
  2. 所有的逻辑封装到这里的函数里
  3. 在 actions 文件中调用这里的函数名
  4. 组件中使用时使用的是 actions 中的函数名
  5. 有参数时加入参数
  6. state 固定参数,指向定义的初始化状态
*/
export const mutations = {
  // 计数器增加
  addNumber(state) {
    console.log(state.number);
    if (state.number >= 10) {
      state.number = 10
    } else {
      state.number++
    }
  },
  // 计数器减少
  subNumber(state) {
    console.log(state.number);
    if (state.number <= 1) {
      state.number = 1
    } else {
      state.number--;
    }
  }
};

store / modules / login.js

const state = {}

const mutations = {}

const actions = {}

const getters = {}

export default {
  state,
  mutations,
  actions,
  getters,
  namespaced: true
}

26. 本地存储

localStorage:存储在本地浏览器中,不会删除

// 设置本地存储
localStorage.setItem([key], [val]);
// 获取本地存储
localStorage.getItem([key]);

sessionStorage:存储在本地浏览器中,窗口关闭会自动删除

// 设置本地存储
sessionStorage.setItem([key], [val]);
// 获取本地存储
sessionStorage.getItem([key]);

监听本地存储

window.onStorag()

27. UI 库

pc端

npm install element-ui --save

移动端

npm install vant --save

28. 扩展补充

1. 设置不同页面的不同title

src / main.js

router.beforeEach((to, from, next)=>{
  if(to.meta.title){
    document.title = to.meta.title;
  }
  next();
})

src / router / index.js

export default new Router({
  routes: [
    { //首页
      path: '/',
      component: indexRouter,
      //此路由的title
      meta: {
        title: '我的世界'
      }
    },
    { //重定向
      path: '*',
      redirect: '/'
    }
  ]
})
2. 设置title,第二个方法
  1. 安装
npm install vue-wechat-title --save
  1. 引入:src / main.js
import vueTitle from 'vue-wechat-title'
Vue.use(vueTitle)
  1. 设置:src / router / index.js
export default new Router({
  routes: [
    { //首页
      path: '/',
      component: indexRouter,
      //此路由的title
      meta: {
        title: '我的世界'
      }
    },
    { //重定向
      path: '*',
      redirect: '/'
    }
  ]
})
  1. 路由出口使用

src / App.vue

<div>
  <router-view v-wechat-title="$route.meta.title"></router-view>
</div>
3. 使用时的不注意
  1. vuex使用时忘记在 main.js 中注册
  2. vuex使用时:在 actions 中注册事件有参数的要带参数(在 context 后面写的参数,经常会忘记)
  3. 数据请求时的配置代理
4. 递归组件

**父组件:**注册使用子组件(递归组件),传入数据

<template>
    <!-- 列表 -->
    <div class="list">
        <v-list :list="list"></v-list>
    </div>
</template>

<script>
import vList from '../../common/list'
export default {
  data() {
    return {
      list: {},
    }
  },
  components:{
    vList
  }
}
</script>

子组件:

  1. 必须要有 name 属性,通过 name 属性才能成功递归
  2. name 属性的值就是递归组件调用时的名字
  3. props 接收父组件的传值时,必须规定数据的类型
  4. 递归组件调用自身时,必须要有 v-if,确保不会陷入死循环
<template>
  <div>
    <ul v-for="(value,key) in list" :key="key">
      <li><span>{{key}}</span></li>
      <!-- 如果数据类型为对象,进行列表循环 -->
      <li v-if="typeof(value)==='object'">
        <!-- 调用自身 -->
        <v-list :list="list[key]"></v-list>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "vList",
  props: {
    list: Object
  }
}
</script>
5. 可视化图表
<template>
  <div id="echarts" class="app"></div>
</template>
<script>
// 引入echarts
import echarts from 'echarts'
export default {
  data() {
    return {
      //图表数据
      option: {
        title: {    //图表标题
          text: "echarts实例"
        },
        tooltip: {},
        legend: {   //
          data: ["分数"]
        },
        xAxis: {    //x轴数据
          data: ["语文","高数","英语","c语言","C#","C++","asp.net","web"]
        },
        yAxis: {},  //y轴数据
        series: [{
          name: '分数',
          type: 'bar',
          data: [60,42,10,84,41,60,82,90]
        }]
      }
    }
  },
  mounted() {
    var myChart = echarts.init(document.getElementById('echarts'));
    myChart.setOption(this.option);
  }
}
</script>
6. 插件
1. core-js --> JavaScript 的模块化标准库
import 'core-js'; // <- at the top of your entry point

Array.from(new Set([1, 2, 3, 2, 1]));          // => [1, 2, 3]
[1, [2, 3], [4, [5]]].flat(2);                 // => [1, 2, 3, 4, 5]
Promise.resolve(32).then(x => console.log(x)); // => 32
2. fingerprintjs2 --> 浏览器指纹采集器 获取用户唯一标识码
if (window.requestIdleCallback) {
    requestIdleCallback(function () {
        Fingerprint2.get(function (components) {
          console.log(components) // an array of components: {key: ..., value: ...}
        })
    })
} else {
    setTimeout(function () {
        Fingerprint2.get(function (components) {
          console.log(components) // an array of components: {key: ..., value: ...}
        })  
    }, 500)
}
3. js-base64 --> base64的编码解码
let latin = 'dankogai';
let utf8  = '小飼弾'
let u8s   =  new Uint8Array([100,97,110,107,111,103,97,105]);
Base64.encode(latin);             // ZGFua29nYWk=
Base64.btoa(latin);               // ZGFua29nYWk=
Base64.btoa(utf8);                // raises exception
Base64.fromUint8Array(u8s);       // ZGFua29nYWk=
Base64.fromUint8Array(u8s, true); // ZGFua29nYW which is URI safe
Base64.encode(utf8);              // 5bCP6aO85by+
Base64.encode(utf8, true)         // 5bCP6aO85by-
Base64.encodeURI(utf8);           // 5bCP6aO85by-
Base64.decode(      'ZGFua29nYWk=');    // dankogai
Base64.atob(        'ZGFua29nYWk=');    // dankogai
Base64.atob(        '5bCP6aO85by+');    // '小飼弾' which is nonsense
Base64.toUint8Array('ZGFua29nYWk=');    // u8s above
Base64.decode(      '5bCP6aO85by+');    // 小飼弾
// note .decodeURI() is unnecessary since it accepts both flavors
Base64.decode(      '5bCP6aO85by-');    // 小飼弾
4. vuescroll --> 自定义滚动条插件
import Vue from "vue";
import vuescroll from 'vuescroll/dist/vuescroll-native';
import 'vuescroll/dist/vuescroll.css';

Vue.use(vuescroll); // install the vuescroll first
Vue.prototype.$vuescrollConfig = {
    mode: 'slide',
    bar: {
        background: '#e59243',
        size: '12px',
        onlyShowBarOnScroll: false,
        showDelay: 500
    },
    rail:{
        gutterOfSide: '10px'
    }
};
5. vue-count-to --> 数据滚动插件
import CountTo from 'vue-count-to';
import Vue from "vue";
import vuescroll from 'vuescroll/dist/vuescroll-native';
import 'vuescroll/dist/vuescroll.css';

Vue.use(vuescroll); // install the vuescroll first
Vue.prototype.$vuescrollConfig = {
    mode: 'slide',
    bar: {
        background: '#e59243',
        size: '12px',
        onlyShowBarOnScroll: false,
        showDelay: 500
    },
    rail:{
        gutterOfSide: '10px'
    }
};
https://www.npmjs.com/package/vue-count-to
6. nprogress --> 顶部进度条
$ npm install --save nprogress
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';

NProgress.start();    // 开始
NProgress.done();    // 结束
https://github.com/rstacruz/nprogress
8. markdown 编辑器
// v-md编辑器文档
https://code-farmer-i.github.io/vue-markdown-editor/examples/base-editor.html

// vue -> markdown编辑器
https://cn.vuejs.org/v2/examples/index.html

// mavonEditor
https://www.jianshu.com/p/04376d0c9ff1
9. 插件总结
https://www.cnblogs.com/lguow/p/9257289.html

29. is 动态组件 - 标签页

<template>
    <div class="app">
        <button @click="changView(1)">view1</button>
        <button @click="changView(2)">view2</button>
        <button @click="changView(3)">view3</button>
        <component :is="view"></component>
    </div>
</template>

<script>
import view1 from '../common/view1';
import view2 from '../common/view2';
import view3 from '../common/view3';
export default {
    data() {
        return {
            view: 'view1'
        }
    },
    components: {
        view1,
        view2,
        view3
    },
    methods: {
        changView(index){
            this.view = "view" + index;
        }
    },
}
</script>

<style scoped>
</style>

30. Vue.set - 数据修改,视图未更新

Vue.set([原数据,索引,新值])

attribute:元素标签的属性

property:元素对象的属性

<template>
    <div class="app">
        <ul>
            <li v-for="item in data" :key="item.id">{{ item.message }}</li>
        </ul>
        <button @click="changeMsg">添加</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            data: [
                {id: "0", message: "html"},
                {id: "1", message: "css"},
                {id: "2", message: "javascript"},
            ],
            index: 0
        }
    },
    methods: {
        // 添加
        changeMsg(){
            // 使用 $set 想数据中添加数据
            this.$set(this.data, index, {
                id: "3",
                message: "Vue"
            });
        }
    }
}
</script>

31. parent children

ref:为子组件指定一个索引名称,通过索引来操作子组件

$parent:直接获取父组件的属性和方法

$children:直接获取子组件的属性和方法,可能会有多个子组件,所以获取的是一个数组

parent.vue

<template>
    <div class="app">
        <button @click="changeMsg">parent-btn</button>
        <p>{{ parentMsg }}</p>
        <children />
    </div>
</template>

<script>
import children from '../common/children';
export default {
    name: 'Parent',
    props: [],
    data() {
        return {
            parentMsg: 'Vue'
        }
    },
    components: {
        children
    },
    methods: {
        // 改变子组件的属性(获取和设置)
        changeMsg(){
            this.$children[0].childrenMsg = "子组件被改变了";
            console.log(this.$children[0].childrenMsg);
        }
    }
}
</script>

children.vue

<template>
    <div class="app">
        <button @click="changeMsg">children-btn</button>
        <p>{{ childrenMsg }}</p>
    </div>
</template>

<script>
export default {
    name: 'Children',
    props: [],
    data() {
        return {
            childrenMsg: "children"
        }
    }
    methods: {
        // 改变父组件的属性(获取和设置)
        changeMsg(){
            this.$parent.parentMsg = "父组件被改变了";
            console.log(this.$parent.parentMsg);
        }
    }
}
</script>

32. Vue事件总线

  1. 创建一个Vue实例
  2. 在实例中通过事件传递数据
// Vue全局事件总线
const EventBus = new Vue();
Vue.prototype.$bus = EventBus;        //全局事件总线
this.$bus.$emit('事件名', ['值']);
this.$bus.$on('事件名', (value)=>{
    console.log(value);
})
this.$bus.$off('事件名');

33. 图片引入方式

1. 
data(){
    return {
        img: require('@/logo.png');
    }
}

2.
import img from '@/img/logo.png';
data(){
    return {
        img: img
    }
}

3. 静态资源引入,直接引入
mounted() {
    this.img = 'static/img/logo.png';
}

34. element 问题

1. element-ui 修改样式不显示
  1. 将 <style> 上的 scoped 删掉
  2. 在 App.vue 中设置全局样式
2. 下拉框不响应

forceUpdate():手动强制刷新

this.$forceUpdate()

35. 刷新部分组件

/* 样式穿透 */
.el-input /deep/ .el-input__inner{
    border: none;
}

/* Vue3 穿透 */
&:deep(.el-input__inner){
    border: none;
}

App.vue

<keep-alive> 一起使用无效

<template>
    <div id="app" @click.stop="dialogyShow">
        <router-view v-if="isAlive"/>
    </div>
</template>

<script>
// vuex
import { mapActions } from 'vuex';
export default {
    name: 'App',
    data() {
        return{
            isAlive: true
        }
    },
    provide() {
        return {
            reload: this.reload
        }
    },
    methods: {
        reload() {
            this.isAlive = false;
            this.$nextTick(function () {
                this.isAlive = true;
            });
        }
    },
}
</script>

index.vue

export default {
  inject: ['reload'],
  methods: {
    handleReload() {
      this.reload(); // 在想要刷新页面的时候调用reload方法
    }
  }
}

36. 使用Echarts

common / echarts.vue

<template>
    <div :id="charData.id" ref="e_chart_s" style="display: inline-block;"></div>
</template>

<script>
import echarts from 'echarts';
export default {
    name: "Echarts",
    props: {
        charData: {
            type: Object
        }
    },
    data() {
        return {
            show: true,
        };
    },
    mounted() {
        this.$refs.e_chart_s.style.width = this.charData.width + "px";
        this.$refs.e_chart_s.style.height = this.charData.height + "px";
        this.init();
    },
    methods: {
        init(){
            let chars = echarts.init(document.getElementById(this.charData.id));
            let option = this.charData.option;
            chars.setOption(option);
        },
        clear(){
            let chars = echarts.init(document.getElementById(this.charData.id));
            chars.clear();
        },
        dispose(){
            let chars = echarts.init(document.getElementById(this.charData.id));
            chars.dispose();
        }
    }
};
</script>

<style lang="less" scoped>
</style>

common / index.js

import Echarts from './Echarts.vue';

export default {
    Echarts
};

main.js

import e_char from './common/index';
import option from './echarts/option';

// 3.全局组件
for(let i in e_char){
    Vue.component(i, e_char[i]);
}
// echarts配置
Vue.prototype.$option = option;

view / demo.vue

<template>
    <div class="template">
        <Echarts :charData="charData" ref="char"></Echarts>
    </div>
</template>

<script>
export default {
    data(){
        return{
            charData: {
                id: "Echarts",
                width: 400,
                height: 400,
                option: this.$option.Contrast(),
            }
        };
    },
    mounted() {
        this.init();
    },
    methods: {
        init(){
            let name = ["全市", "全校", "班级"];
            let data = [10,20,30];
            this.$refs.char.dispose();
            this.charData.option = this.$option.Contrast(name, data);
            this.$refs.char.init();
        },
    }
};
</script>

option.js

let Contrast = (xName, data) => {
    return {
        silent: true,
        zgrid: {},
        xAxis: [],
        yAxis: [],
        series: []
    };
};

export default {
    Contrast
};

37. element - upload 上传

<template>
    <el-upload
        class="upload-demo"
        drag
        action=""
        accept=".xlsx,.xls"
        :auto-upload="false"
        :on-change="onChange">
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">上传模板</div>
    </el-upload>

    <button @click="upload">上传</button>
</template>
<script>
export default {
    data() {
        return {
            formData: {}
        }
    }
    methods: {
        // 文件选取
        onChange(file){
          this.$set(this.formData,'file',file)
        },
        // 上传文件
        upload(){
            const formData = new FormData();
            formData.append("file", this.formData.file.raw);
            formData.append("organizeId", this.$store.getters.userInfo.organizeId);
            formData.append("classId", JSON.parse(sessionStorage.getItem("nameSelect")).id);
            /*
                1. 上传接口
                2. 参数 formData
            */
        }
    }
}

38. vue.config.js

publicPath
  • Type: string

  • Default: /

    例如,如果你的应用被部署在 https://www.my-app.com/my-app/,则设置 publicPath/my-app/

    这个值也可以被设置为空字符串 ('') 或是相对路径 ('./'),这样所有的资源都会被链接为相对路径,这样打出来的包可以被部署在任意路径

outputDir
  • Type: string

  • Default: 'dist'

    build 时生产环境的目录,构建时会清除目录

    使用 --no-clean 可关闭

assetsDir
  • Type: string

  • Default: ''

    静态资源目录

indexPath
  • Type: string

  • Default: 'index.html'

    生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径

pages
  • Type: Object

  • Default: undefined

    在 multi-page 模式下构建应用。每个“page”应该有一个对应的 JavaScript 入口文件。其值应该是一个对象,对象的 key 是入口的名字,value 是:

    • 一个指定了 entry, template, filename, titlechunks 的对象 (除了 entry 之外都是可选的);
    • 或一个指定其 entry 的字符串。
    module.exports = {
      pages: {
        index: {
          // page 的入口
          entry: 'src/index/main.js',
          // 模板来源
          template: 'public/index.html',
          // 在 dist/index.html 的输出
          filename: 'index.html',
          // 当使用 title 选项时,
          // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
          title: 'Index Page',
          // 在这个页面中包含的块,默认情况下会包含
          // 提取出来的通用 chunk 和 vendor chunk。
          chunks: ['chunk-vendors', 'chunk-common', 'index']
        },
        // 当使用只有入口的字符串格式时,
        // 模板会被推导为 `public/subpage.html`
        // 并且如果找不到的话,就回退到 `public/index.html`。
        // 输出文件名会被推导为 `subpage.html`。
        subpage: 'src/subpage/main.js'
      }
    }
    

39. vue3依赖注入

父传子孙数据:provide
子孙得到数据:inject

父 -> 子 传值

// 父组件
import { ref, provide } from 'vue'
  setup(){
    // 函数, 值
    provide('moneyInfo', 1000)
    return{
      moneyInfo
    }
  }

//孙子组件
import { inject } from 'vue'
setup(){
  const moneyInfo = inject('moneyInfo')
  return{
    moneyInfo
  }
}

子 -> 父 传值

//父组件
import {ref,provide} from 'vue'
setup(){
  provide('money',money)
  // 封装接收孙子组件数据的函数
  const money=(val)=>{
    console.log('子孙组件传递的数据',val)
  }
}

//子孙组件
<div @click='handleSend'>点击传值</div>
import {inject} from 'vue'
setup(){
  const money = inject('money')

  const handleSend=() => {
    money(200)
  }
}

productionSourceMap

  • Type: boolean

  • Default: true

    如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。

引入组件报错

SyntaxError: The requested module ‘/@/components/popover/Popover.vue’ does not provide an export named ‘[组件]’

不提供名为【组件】的导出

import { Popover } from '/@/components/popover/Popover.vue';

引入组件时错误的使用了 {}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值