Vue 快速复习

基本语法

模板语法
1. {{ msg }}
2. v-html="attName"
3. v-bind:xxx="attName" or :xxx="attName"  ==> <tag xxx="val" />
4. 在模板中使用 js 表达式,比如 {{ ok ? "YES":"NO" }}, v-html="ok ? '<hr/>' : '<br/>'"

条件渲染
1. v-if="attName" ==> 惰性、真正的条件渲染(基于 dom 增删)、有切换开销,适用于条件改变比较少的元素
2. v-else => <p v-if="attName">true</p><p v-else>false</p>
3. v-show="attName" ==> 元素总是会被渲染,基于 css 切换显示状态、有初始渲染开销,适用于条件切换频繁的元素

列表渲染
<tag v-for="item in items" :key="item.id">{{item.title}}</tag>
<tag v-for="(item,index) in items" :key="item.id|index">{{item.title}}</tag>
“推荐在任何可行的时候为 v-for 提供一个 key attribute”
ref:https://cn.vuejs.org/guide/essentials/list.html#maintaining-state-with-key

事件处理
<button v-on:click="cnt += 1">click me</button>
<button @click="clicked">click me</button>
<button @click="m2('blabla')">m3</button>
<li v-for="item in items" :key="item.id" @click="handle(item.id)">{{item.title}}</li>
-------------------------
export default {
  name: "",
  data() {
    cnt: 1
  },
  methods: {
    clicked(event) {
      console.log("clicked!", event) // 原生 event 对象
    },
    m3(data) {
      console.log(data)
    },
    handle(id) {
      console.log(id)
    }
  }
}

表单输入绑定
<input v-model="username" />
<input v-model.lazy="username" />    只在失去焦点或会车时进行数据同步,减少同步开销
<input v-model.trim="username" />    去掉输入的首尾空格

组件相关

单文件组件

<template></template>   必须
<script>                非必须
  export default {
    name: "组件名"
  }
</script>
<style scope></style>   非必须

加载组件

1. 引入组件 import MyComp from './comps/MyComp.vue'
2. 挂载组件 components: { MyComp }
3. 显示组件 <my-comp /> 或 <MyComp />

数据交互之正向传值(将数据从父组件传递到子组件)

<h3 style="color: red">{{ title }} - {{ msg }} - {{ cnt }}</h3>
export default {
  name: 'MyComp',
  props: {
    msg: {
      type: String,
      default: 'null',
    },
    title: {
      type: String,
      default: 't',
    },
    cnt: {
      type: Number,
      default: 0,
    },
  }
}
----
<!-- 使用 v-bind:attName="attr" 给子组件传值 -->
<my-comp :msg="msg" title="消息" :cnt="1" />
import MyComp from './MyComp.vue'
export default {
  name: 'App',
  data() {
    return {
      msg: '小坏蛋',
    }
  },
  components: {
    MyComp,
  },
}
----
注意:
1. props 中定义的属性数据类型可以为 String|Number|Boolean|Array|Object|Function
2. props 中定义的属性数据类型为数组或对象时,默认返回值需要返回值是方法。比如
props: {
  ids: {
    type: Array,
    default: function() { return [] }
  }
}

数据交互之使用自定义事件反向传值

  <button @click="handleClick">click-me</button>
  methods: {
    handleClick() {
      // 子组件通过“自定义事件名”将消息传递给父组件
      this.$emit('onMyEvent', this.message)
    },
  },
--------------
  <h3>data={{ msg }}</h3>, <my-sub-comp @onMyEvent="handleData" />
  methods: {
    handleData(data) {
      this.msg = data
    },
  },

组件生命周期

主要包含八个生命周期钩子函数:
beforeCreate:创建还没开始创建
created:     创建已经创建完成,单位显示出来
beforeMount: 组件渲染之前
mounted:     组件已经渲染完毕,常用来做网络请求获取数据,获取数据后进行数据渲染 ⭐
beforeUpdate:组件更新之前
updated:     组件更新完
beforeUnmount:组件卸载之前,卸载消耗性能的资源,比如定时器
unmounted     组件已经卸载

参考:https://cn.vuejs.org/guide/essentials/lifecycle.html#lifecycle-diagram

引用第三方

Vue 官方整理的资源列表:https://github.com/vuejs/awesome-vue#menu,在使用时注意是否支持 vue2 或 vue3。

Swiper

开源、免费、强大的触摸滑动插件;

Axios

简单使用

npm i axios --save
npm i querystring --save
局部使用
-------
import axios from 'axios'
import querystring from 'querystring'
export default {
  name: 'AxiosTest',
  data() {
    return {
      title: '',
    }
  },
  mounted() {
    
    axios({
      method: 'get',
      url: 'http://x.y.z/a/b/get/1024',
    }).then((res) => {
      this.title = res.data.prodName
    })

    axios({
      method: 'post',
      url: 'http://x.y.z/a/b/add/1024',
      // 使用 querystring 工具类转为 json 字符串
      data: querystring.stringify({
        prodName: '男士内裤',
        prodPrice: 50,
      }),
    }).then((res) => {
      console.log(res.data.message)
    })
    
    //快捷操作
    axios.get('url').then(res=>{ ... })
    axios.post('url', data: ... ).then(res=>{ ... })
  },
}
                                       
全局使用
------- main.js ---------
import axios from 'axios'
                                       
//...

var app = createApp(App)
app.config.globalProperties.$axios = axios   //挂载到全局属性集合中
app.mount('#app')
---- 使用
this.$axios.get('url').then(res => { ... })

axios 封装

  1. request.js 工具类
import axiox from 'axios'
import querystring from 'querystring'

const processErrorResponse = (status, info) => {
  switch (status) {
    case 400:
      console.log('语义有误')
      break
    case 401:
      console.log('服务器认证失败')
      break
    case 403:
      console.log('服务器拒绝访问')
      break
    case 404:
      console.log('地址错误')
      break
    case 500:
      console.log('服务器内部错误')
      break
    case 502:
      console.log('服务器无响应')
      break
    default:
      console.log(info)
  }
}

const instance = axiox.create({
  baseURL: 'https://x.y.z/api/',
  timeout: 1000,
  headers: { 'X-Custom-Header': 'foobar' },
})

//请求拦截器
instance.interceptors.request.use(
  (config) => {
    // 如果是 post 请求,则将其中的对象转为字符串
    if (config.method === 'post') {
      config.data = querystring.stringify(config.data)
    }
    // config 对象包含请求的所有信息
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

//响应拦截器
instance.interceptors.response.use(
  (response) => {
    return response.status === 200
      ? Promise.resolve(response)
      : Promise.reject(response)
  },
  (error) => {
    const { response } = error
    processErrorResponse(response.status, response.info)
  }
)

export default instance

参考 ruoyi-vue/ruoyi-uirequest.js

  1. 编写 api/user.js(参考 ruoyi-vue)
import request from '@/utils/request'

export function list() {
  return request({
    url: '/user/list',
    method: 'get',
  })
}

export function get(id) {
  return request({
    url: '/user/' + id,
    method: 'get',
  })
}

export function add(name, age, email, password) {
  return request({
    url: '/user',
    method: 'post',
    data: {          // 请求拦截器会进行处理
      name,
      age,
      email,
      password,
    },
  })
}

参考 ruoyi-vue/ruoyi-uiapi 目录

其他:

跨域问题及解决

同源策略是什么?是浏览器的一项安全策略,它只允许 JS 代码请求和当前所在服务器域名、端口和协议相同的接口。

解决方法:

  1. 后台:配置 CORS;
  2. 前台:使用代理
// vue.config.js
  devServer: {
    proxy: {
      '/api': {
        target: 'http://a.d.s.w',
        changeOrigin: true,
      },
    },
  },
      
// vite.config.js
  server: {
    port: 5111,
    host: true,
    open: true,
    proxy: {
      '/api': {
        target: 'http://a.d.s.w',
        changeOrigin: true,
      },
    },
  },

路由配置

路由基本使用

安装

npm install vue-router --save

在 views 下创建两个文件 HomeView.vue、AboutView.vue。

创建 @/router/index.html,创建并配置路由对象

---> @/router/index.html
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/',
      component: HomeView,
    },
    {
      path: '/about',
      component: () => import('../views/AboutView.vue'),
    },
  ],
})

export default router

在入口文件中引入并使用创建的 router 对象

import router from './router'

const app = createApp(App)
app.use(router)
app.mount('#app')

在 App.vue 中编写路由导航

<template>
  <router-link to="/">Home</router-link> |
  <router-link to="/about">About</router-link>
  <br />
  <router-view />
</template>

路由传参

在路由链接中添加路由参数,在目标组件中取出路由参数。

添加路由链接

<template>
  <li v-for="i in idList" :key="i">
    <router-link :to="'/info/' + i">{{ i }}</router-link>
  </li>
</template>

添加路由

    {
      //  使用 :xxx 的方式指定路由参数,xxx 为参数名
      path: '/info/:id',
      component: () => import('../views/AboutInfo.vue'),
    },

创建子组件

<template>
  <h3>info, id = {{ id }}</h3>
</template>
<script>
export default {
  name: 'AboutInfo',
  data() {
    return {
      id: '',
    }
  },
  created() {
    // 从 this.$route.params 中获取路由上的参数
    this.id = this.$route.params.id
  },
}
</script>

嵌套路由

嵌套路由,比如菜单。

创建两个组件 P1View.vue、P2View.vue,将其作为子路由添加到 /

    {
      path: '/',
      name: 'home',
      redirect: '/p1',   // 重定向 / 到 /p1
      component: HomeView,
      children: [        // 子路由
        {
          path: 'p1',    // 开头不加斜杠
          component: () => import('@/views/P1View.vue'),
        },
        {
          path: 'p2',
          component: () => import('@/views/P2View.vue'),
        },
      ],
    },

在 HomeView.vue 中添加子路由链接和路由视图

<template>
  <h3>首页</h3>
  <router-link to="/p1">饮食区</router-link>|
  <router-link to="/p2">娱乐区</router-link>

  <!-- 注: 匹配路由的组件会被渲染到这里 -->
  <router-view />
</template>

https://router.vuejs.org/guide/#html

状态管理

一个集中式的状态管理方案,使组件之间数据交互更加方便。

简单使用

npm i vuex --save

创建 @/store/index.js,创建并暴露 store 实例

---> @/sotre/index.js
import { createStore } from 'vuex'

export default createStore({
  state: {
    cnt: 128,
  },
})

使用 store 实例,在 main.js 中

import store from './store'

createApp(App).use(store).mount('#app')

在任何组件中使用 cnt 这个属性

  <p>app.vue, cnt = {{ $store.state.cnt }}</p>

如果在一个组件中需要多次读取 store 中的属性,如何进行简化?

答:使用 vuex 提供的 mapState。

<p>{{ cnt }}</p>

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

getter - 数据过滤

export default createStore({
  state: {
    cnt: 128,
  },
  getters: {
    getCnt(state) {
      return state.cnt > 0 ? state.cnt : 'cnt无效'
    },
  },
}
<!-- 直接读取 -->
<p>getter, {{ $store.getters.getCnt }}</p>

<!-- mapGetters 方式读取 -->
<p>{{ getCnt }}</p>

<script>
import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters(['getCnt']),
  },
}
</script>

mutaions - 修改数据

export default createStore({
  state: {
    cnt: 128,
  },
  mutations: {
    incrCnt(state) {
      state.cnt++
    },
    addCnt(state, num) {
      state.cnt += num
    },
  },
}
    <!-- 直接调用 -->
    <p>mutation, <button @click="$store.commit('incrCnt')">incr</button></p>
    <p><button @click="$store.commit('addCnt', 10)">add 10</button></p>

    <!-- 使用 mapMutations 抽取 $store 中的 mutations 为普通函数,直接调用即可 -->
    <p><button @click="incrCnt">incr by mapMutations</button></p>
    <p><button @click="addCnt(10)">add 10 by mapMutations</button></p>
<script>
import { mapMutations } from 'vuex'
export default {
  methods: {
    ...mapMutations(['incrCnt', 'addCnt']),
  },
}
</script>

注意:mutations 处理器中的方法必须是同步的,参考

比如,下边的例子在 mutations 方法中使用异步,不推荐这种方式

mutations: {
    reqNumAadd2Cnt(state) {
        axios.get('http://localhost:8080/robots.txt').then((res) => {
            state.cnt += parseInt(res.headers['content-length'])
        })
    },
}

action - 异步修改

export default createStore({
  state: {
    cnt: 128,
  },
  mutations: {
    addCnt(state, num) {
      state.cnt += num
    },
  },
  actions: {
    //异步处理
    asyncChgCnt({ commit }) {
      axios.get('http://localhost:8080/robots.txt').then((res) => {
        // 交给 mutation 同步处理
        commit('addCnt', parseInt(res.headers['content-length']))
      })
    },
  },
}
<button @click="$store.dispatch('asyncChgCnt')">req network and chg cnt - 1</button>
<button @click="asyncChgCnt">req network and chg cnt - 2</button>
---------------
<script>
  mutations: {
    addCnt(state, num) {
      state.cnt += num
    },
  },
  actions: {
    //异步处理
    asyncChgCnt({ commit }) {
      axios.get('http://localhost:8080/robots.txt').then((res) => {
        // 交给 mutation 做同步处理
        commit('addCnt', parseInt(res.headers['content-length']))
      })
    },
  },
</script>
------------- vite.config.js --------------
  server: {
    port: 8080,
    open: true,
    proxy: {
      '/robots.txt': {
        target: 'http://baidu.com',
        changeOrigin: true,
      },
    },
  },

pinia

https://pinia.vuejs.org/zh/introduction.html#why-should-i-use-pinia

Vue3 新特性

6 大亮点:

  1. Composition API:组合 API ⭐;
  2. 性能比 Vue2 强;
  3. 可以“剪辑”掉无用模块,仅打包所需模块;
  4. “碎片”、“悬念”;
  5. 更好的 TS 支持(Vue3 底层是 TS 写的);
  6. 暴露了自定义渲染 API;

组合式 API — setup()

我们可以使用响应式 API 来声明响应式的状态,在 setup() 函数中返回的对象会暴露给模板和组件实例。

参考:https://cn.vuejs.org/api/composition-api-setup.html#basic-usage

注意:setup() 在 beforeCreate 生命周期钩子之前执行。

ref 和 reactive 定义属性

  • ref:声明简单变量,比如字符串、数字
  • reactive:声明复杂变量,比如数组、对象

可使用 ref 和 reactive 声明变量,并通过 return 将其暴露出去。与 vue2 中的 data(){...} 类似。

  <p>{{ lang }}</p>
  <ul>
    <li v-for="(cty, index) in cties.countries" :key="index">{{ cty }}</li>
  </ul>
---------------------
import { ref, reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    const lang = ref('english')
    const cties = reactive({
      countries: ['China', 'England', 'Indian', 'Thailand'],
    })
    return {
      lang,
      cties,
    }
  },
}

定义函数

<p>{{ lang }}</p>
<button @click="clickHandler">click me</button>
-------------------
import { ref, reactive } from 'vue'
export default {
  components: {
    TheWelcome,
  },
  name: 'App',
  setup() {
    const lang = ref('English')
    const clickHandler = () => {
      lang.value = 'Chinese'           // 注意:在 setup 内部需要使用【变量名.value】的方式获取属性
      console.log('clicked!')
    }
    return {
      lang,
      clickHandler,
    }
  },
}

使用 props

--------------- 传参 ----------------->
----> @/App.vue
<TheWelcome msg="Hello Everyone!" />
-------------------
import TheWelcome from './components/TheWelcome.vue'
export default {
  components: {
    TheWelcome,
  },
}

--------------- 接参 ----------------->
---> @/components/TheWelcom.vue
<p>{{ message }}</p>
------------------
export default {
  props: {               // 声明入参
    msg: String,
  },
  setup(props) {
    const message = props.msg
    return {
      message,           // es6 语法
    }
  },
}
</script>

使用 ctx(当前实例对象)

在 setup 中不能使用 this,而应该用 ctx,因为 ctx 就代表当前实例对象

setup(props, ctx) {
  console.log(ctx)          // ctx 就代表当前实例对象
},
------------> 打印该 ctx
Object:
- attrs: (…)
- emit: (…)
- expose: (exposed) => {…}
- slots: (…)
- get attrs: ƒ attrs()
- get emit: emit() {         return (event, ...args) => {…}
- get slots: ƒ slots()
- [[Prototype]]: Object

生命周期函数

可以在 setup 内部使用 6 中生命周期钩子

组件的钩子 setup 内部钩子
beforeCreate x
created x
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
export default {
  setup() {
    //可存在多个相同的钩子函数
    onMounted(() => {
      console.log('mounted - 1')
    })
    onMounted(() => {
      console.log('mounted - 2')
    })
  },

provide / inject

  • 使用 provide() 和 inject() 可以实现嵌套组件之间的数据传递;
  • 这两个函数只能在 setup 函数中使用;
  • 只能从父组件传值给子组件:父组件使用 provide() 函数向下传递数据,子组件中使用 inject() 函数获取上层传递过来的数据;
import { provide } from 'vue'
setup() {
  provide('myQQ', '51383719')
}
-----------------------
import { inject } from 'vue'
setup() {
  const myQQ = inject('myQQ')
  console.log(myQQ)
}

Vue3 集成 ElementPlus

https://element-plus.org/zh-CN/guide/design.html

加载方式

  1. 全量加载
  2. 按需加载(推荐)

....

posted @ 2023-01-16 00:05  egu0o  阅读(22)  评论(0编辑  收藏  举报