Vue3.0實戰項目


vue3.0從零開始到實戰項目

vue3.0中文官網連接:https://v3.cn.vuejs.org/

 

一、安裝node.js

1. 官方文檔:https://nodejs.org/en/

安裝版本必須大於12.0,建議直接安裝最新穩定版本

2. 安裝 node.js 的同時,一般會自動安裝 npm 的,所以不用單獨安裝 npm 了(如有需要,也可以命令行安裝:npm install -g)

3. 查看版本命令行:node -v 、 npm -v

 

二、全局安裝vue腳手架

npm install @vue-cli -g

官方文檔:https://cli.vuejs.org/zh/guide/

如果你安裝的是舊版的vue-cli,需要提前卸載之后再重新安裝 @vue-cli,卸載:

npm uninstall vue-cli -g

 

三、初始化項目

版本查看:vue -V

版本升級:npm update -g @vue-cli

創建項目:vue create projectname

注意:項目名稱不能大寫有字母

進入項目目錄:cd projecname

運行項目:npm run serve

項目目錄結構:

1. node_modules 依賴包,如果刪除了,可以使用命令:npm install 安裝

2. public 存放整個項目默認的 HTML 模板

3. src 源代碼目錄(入口文件:main.js)

4. .browserslistrc 

5. .editorconfig 編輯器的默認配置

6. .eslintrc.js 

7. .gitignore 

8. babel.config.js 配置 babel 

9. package.json 

10. package-lock.json 

11. README.md

以下是手動選擇依賴初始化:

 

 

 

 

 

運行項目:

cd projectname

npm run serve

 

 

 

四、vue 3.0 + vite 篇

Vite 是一個 web 開發構建工具,由於其原生 ES 模塊導入方法,它允許快速提供代碼。

通過在終端中運行以下命令,可以使用 Vite 快速構建 Vue 項目。

 

$ npm init vite-app <project-name>

$ cd <project-name>

$ npm install

$ npm run dev

 

 

 

 

安裝項目所需插件,比如:

安裝項目生產依賴:npm vie vue-router@next vuex@next element-plus axios -D

安裝項目開發依賴:npm i sass -S

element-plus文檔:https://element-plus.gitee.io/#/zh-CN

安裝路由 Router

# 查看 vue-router 版本

$ npm vie vue-router versions

# 指定版本下載

$ npm i -D vue-router@4.0.0-beta.11

 

 

 

 

 

 

 

 

 

 

 

 

 以下是vue3.0的知識點:

Object.defineProperty => Proxy

重構了虛擬DOM

OptionApi => Composition API

setup 是什么?

setup 實際上是一個組件的入口,它運行在組件被實例化的時候,props 屬性被定義之后,實際上等價於 vue2.x 版本的 beforeCreate 和 Created 這兩個生命周期。

setup 接受兩個參數,第一個是 props ,第二個是 context

 

setup(props, ctx) {
    console.log(props, ctx)
}
let Child = { template: `<div>{{title}}</div>`,
 setup(props, context) { console.log(props) } } let App = { template: `<div class="container"><Child title="test props"/></div>`, components: { Child } }
Vue.createApp().mount(App,
'#app')

 

reactive

const { reactive, toRefs } = Vue let App = { template: `<div class="container">count: {{count}}<button @click="handlerCountAdd"> Click ++ </button></div>`, setup() { const state = reactive({ count: 0 }) const handlerCountAdd = () => { state.count++ } return { ...toRefs(state), handlerCountAdd } } } Vue.createApp().mount(App, '#app')

 

toRefs

vue3 提供的 ref 讓我們有機會創建單個的響應式的對象,在 setup 函數中 return 出去之后,在模板中可直接訪問

const App = { template: `<div class="container">{{value}}</div>`,
 setup() { const value = ref(1) return { value } } } Vue.createApp().mount(App, '#app')
const App = { template: `<div class="container">{{state.value}}</div>`,
 setup() { const state = reactive({ value: 'reactive' }) return { state } } } Vue.createApp().mount(App, '#app')
const App = { template: `<div class="container">{{value}}</div>`,
 setup() { const state = reactive({ value: 'reactive' }) return toRefs(state) } } Vue.createApp().mount(App, '#app')

 

反轉字符串 demo

let App = { template: `<div class="container">value: <input v-model="value"/><br/>rvalue: {{rvalue}}</div>`,
 setup() { const state = reactive({ value: '', rvalue: computed(() => state.value.split('').reverse().join('')) }) return toRefs(state) } } Vue.createApp().mount(App, '#app')

 

數據響應式

在 Vue3 中實現數據響應式的方案由 Vue2 中的 Object.defineProperty 換成了 Proxy,關於數據響應式的Api上邊說到了一些,還剩下 effect 和 watch 沒有提及到,effect 是數據響應式中重要的一部分,watch 和 computed 都是基於 effect 的。

let App = { template: `<div class="container"> count: {{count}} <button @click="handlerCountAdd"> Click ++ </button></div>`, setup() { const state = reactive({ count: 0, value: 1 }) const handlerCountAdd = () => { state.count++ } watch( () => state.count, val => { console.log('watch', state.count) console.log('watch', state.value) } ) effect(() => { console.log('effect', state.count) console.log('effect', state.value) }) return { ...toRefs(state), handlerCountAdd } } } Vue.createApp().mount(App, '#app')

effect 在響應式數據變化的時候就會執行,執行次數根據響應式數據的個數來決定

let App = { template: `<div class="container"><button @click="handlerCountAdd"> Click ++ </button></div>`, setup() { const r = ref(1) const s = ref(1) const t = ref(1) const handlerCountAdd = () => { r.value *= 1 s.value *= 2 t.value *= 3 } effect(() => { console.log('effect', [r.value, s.value, t.value]) }) return { handlerCountAdd } } } Vue.createApp().mount(App, '#app')

而 watch 則點擊一次 ,只會觸發執行一次

let App = { template: `<div class="container"><button @click="handlerCountAdd"> Click ++ </button></div>`, setup() { const state = reactive({ count: 0, value: 1 }) const r = ref(1) const s = ref(1) const t = ref(1) const handlerCountAdd = () => { r.value *= 1 s.value *= 2 t.value *= 3 } watch([r, s, t], val => { console.log('watch', val) }) return { handlerCountAdd } } } Vue.createApp().mount(App, '#app')

 

生命周期

beforeCreate => setup(替代) created => setup(替代) beforeMount => onBeforeMount mounted => onMounted beforeUpdate => onBeforeUpdate updated => onUpdated beforeDestroy => onBeforeUnmount destroyed => onUnmounted errorCaptured => onErrorCaptured

 

全局配置

Vue2.x 創建實例並且掛載 DOM 

import Vue from "vue"; import App from './App.vue'

new Vue({ render: (h) => h(App) }).$mount("#app");

Vue3.0 新增 api ===> createApp 創建實例

createApp 會產生一個 app 實例,該實例擁有全局的可配置上下文 import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app')

 

vue 函數

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

傳了兩個屬性

v-model:selectKeys = "selectKeys"
import {reactive,toRef } from 'vue export default{ setup(props,ctx){ //默認執行一次 //頁面使用 state.selectKeys const state = reactive({ //attr slots emit selectKeys:0 }) //1.直接使用 return { selectKeys:state.selectKeys } //2.導出,頁面上直接使用,數據響應式還帶解構 return { ...toRefs(state) } onMounted(()=>{ }) } }

 

監聽路由變化

import {reactive,toRef,watch } from 'vue import {useRoute} from 'vue-router' export default{ setup(props,ctx){ const state = reactive({ //attr slots emit selectKeys:0 }) //1.watch監控路由變化 watch(()=>route.path,(newValue)=>{ state.selectKeys = [newValue] }) //2.computed監控路由變化 const selectKeys = computed(()=>{ return [route.path] }) return { selectKeys } } }

 

vuex

import {reactive,toRef,watch ,computed} from 'vue' import {useRoute} from 'vue-router' export default{ setup(props,ctx){ const route = userRoute() const store = useStore() const state = reactive({ //attr slots emit
            selectKeys:0 }) //1.watch監控路由變化
        watch(()=>route.path,(newValue)=>{ state.selectKeys = [newValue] }) //2.computed監控路由變化
        const selectKeys = computed(()=>{ return [route.path] }) //ref 把普通值變成包裝后的結構,將屬性變成響應式
        // ref(store.getters.allTime)

        return { selectKeys, allTime:ref(store.getters.allTime) } } } //store.js
import {createStore} from 'vuex export default { state:{ }, getters:{ allTime:()=>{ return 0 } }, mutations:{ }, actions:{ }, modules:{ } }

 

組件通信

import {reactive,toRef,watch ,computed} from 'vue' import {useRoute} from 'vue-router' import moment from 'moment' export default{ setup(props,ctx){ const state = reactive({ form:{ date:moment(Date.now()).format('YYYY-MM-DD') } }) //方法函數
        const onSubmit =()=>{ //傳給父組件
            this.$emit('handlePlan',state.form) } return { ...toRefs(state), onSubmit } } } //父組件
<Child @handlePlan="handlePlan" />
 import {reactive,toRef,watch ,computed} from 'vue' import {useRoute} from 'vue-router' import moment from 'moment' export default{ setup(props,ctx){ const state = reactive({ form:{ date:moment(Date.now()).format('YYYY-MM-DD') } }) const handlePlan = (plan)=>{ console.log(plan) } return { handlePlan } } }

 

封裝 API

// 環境變量
VUE_APP_URL = 'http://www.xxx.com:3000'
import axios from 'axios const instance = axios.create({ baseURL:process.env.VUE_APP_URL, timeout:3000 }) instance.interceptors.request.use((config)=>{ return config }) instance.interceptors.response.use((res)=>{ return res.data.data },err=>{ return Promise.reject(err) }) export function request(opts){ return instance(opts) }
//request.js
import {request } from '../utils/axios' export function getPlanList(){ return request({url:'/plan',method:'get'}) } export function addPlan(data){ return request({url:'/plan',method:'post',data}) } export function deletePlan(){ return request({url:'/plan',method:'delete',params:{id}}) } //action_type.js
export const SET_PLAN_LIST = 'SET_PLAN_LIST' export const ADD_PLAN = 'ADD_PLAN' export const DELETE_PLAN = 'DELETE_PLAN'

//store.js
import {createStore} from 'vuex' export * as types from './action_type' import * as api from './request' export default { state:{ }, getters:{ allTime:()=>{ return 0 } }, mutations:{ [type.ADD_PLAN](state,payload){ state.planList = [...state.planList,payload] }, [type.DELETE_PLAN](state,payload){ state.planList.filter(item=>{ return item._id !=payload._id }) }, [type.SET_PLAN_LIST](state,payload){ }, }, actions:{ //restful api根據不同方法返回不同的資源
 async [type.ADD_PLAN]({commit},payload){ let plan = await api.addPlan(payload) commit(type.ADD_PLAN,plan) }, async [type.DELETE_PLAN]({commit},payload){ let plan = await api.deletePlan(payload) commit(type.DELETE_PLAN,plan) }, async [type.SET_PLAN_LIST]({commit},payload){ let plan = await api.getPlanList(payload) commit(type.SET_PLAN_LIST,plan) }, }, modules:{ } }

 

使用數據

import {reactive,toRef,watch ,onMounted,onUpdated,compile,computed} from 'vue' import {useStore} from 'vuex' import moment from 'moment' import * as types from '@/store/action_types' export default{ setup(props,ctx){ const store = useStore() // const state = reactive({
        // planList:store.state.planList //這樣取的是默認值
        // })
 onMounted(()){ store.dispatch(types.SET_PLAN_LIST) } //時間格式化方法
        const formatDate = (value)=>{ return moment(value).format('YYYY-MM-DD') } return { ...toRefs(state.store), formatDate } } }

 

簡版 vue

//1.創建虛擬節點,將虛擬節點轉化為真實節點 //2.組件的實現 setup //3.reactive api實現effect //4.diff算法 //5.vite
 let { render} = Vue const state = { count:0 } const vnode = { tag:'div', props:{color:'red'}, children:[ { tag:'p', props:{color:'blue}, children:[ 'vue@3-計數器' ] }, { tag:'p', props:{ onClick:()=>{ alert(state.count) } } children:[ 'vue@3-計數器' ] } ] } render(vnode,app) export function render(vnode,container){ // 渲染頁面的方法叫patch //1.第一次渲染 2.dom-diff patch(null,vnode,container) } /** * n1 老的虛擬節點 * n2 新的虛擬節點 * container 容器 */ function patch(n1,n2,container){ //組件的虛擬節點是一個對象,tag是一個對象 //如果是組件,tag可能是個對象 //后續diff可以執行這個方法 if(typeof n2.tag ==='string'){ //標簽 mountElement(n2,container) }else if(typeof n2.tag==='object'){ } } function mountElement(vnode,container){ const { tag,children,props } = vnode //虛擬節點和真實節點做映射關系 let el = (vnode.el = nodeOps.createElement(tag)) if(Array.isArray(children)){ mountChild(children,el) }else{ nodeOps.hostSetElementText(el,children) } container.insert(el,container) } function mountChild(children,container){ for(var i=0;i<children.length;i++){ let child = children[i] patch(null,child,container) } } //節點操作方法 exoprt const nodeOps = { //插入元素節點 insert(child,parent,anchor){ if(anchor){ parent.insertBefore(child,anchor) }else{ parent.appendChild(child) } }, //移除節點 remove(child){ const parent = child.parentNode; parent && parent.removeChild(child) }, //創建節點 createElement(tag){ return document.createElement(tag) }, //設置文本內容 hostSetElementText(el,text){ el.textContent = text } }

 

未完待續。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM