05项目启动 搭建vue3工程化项目
vite项目创建过程
- 下载node.js 选择LTS稳定版本 node -v验证是否安装成功
- vs code 安装插件 volar
- npm init @vitejs/app 创建vite项目 根据提示进行选择
Project name: 项目名称
Select a framework: 选择vue框架
Select a variant: 选择vue
-
npm install && npm run dev 项目启动
-
安装vue-router 和 Vuex
npm install vue-router@next vuex@next
我们所有工程化体系都是基于Node.js生态;我们使用VS Code + volar编辑器 + 语法提示工具作为上层开发工具;使用Vite作为工程化工具;使用Chrome进行调试
项目规范
- 组织结构
├── src
│ ├── api 数据请求
│ ├── assets 静态资源
│ ├── components 组件
│ ├── pages 页面
│ ├── router 路由配置
│ ├── store vuex数据
│ └── utils 工具函数
07 巧妙的响应式: 深入理解vue 3的响应式机制
vueuse 工具包
function useStorage(name, value=[]){
let data = ref(JSON.parse(localStorage.getItem(name)|| value))
watchEffect(()=>{
localStorage.setItem(name,JSON.stringify(data.value))
})
return data
}
vue中好多useXxx包都是基于组合Api搞出来的
npm install @vueuse/core
全屏
<template>
<h1 @click="toggle">click</h1>
</template>
<script setup>
import { useFullscreen } from '@vueuse/core'
const { isFullscreen, enter, exit, toggle } = useFullscreen()
</script>
commonjs / ES Module
commonjs
-
导出用 exports 或 module.exports
exports指向 module.exports 最终导出由module.exports决定 exports只是指向
exports.name = "abc" exports.auth = "xxx" exports.say = function(){} module.exports = {} //会清空 -
导入用 required("xx.js")
const demo = required("xx.js) demo.name
ES Module
export 导出 import 导入
export name = "aaa"
export default = {} //只有一个default
import {name} from "./a.js"
import defaultvalue from "./a.js"
const a = import("./staticfil") //返回Promise对象 实现懒加载
a.then(res=>{})
import 'module' //不导出 只运行一次
10vuex
vuex的使用
- 创建store对象
//src/store.vue
import { createStore } from 'vuex'
const store = createStore({
state () {
return {
count: 666
}
},
getters:{ //可以直接用在computed中
double(state){
return state.count*2
}
},
mutations: {
add (state) {
state.count++
}
}
})
export default store
- 注册到vue app实例上
const app = createApp(App)
app.use(store)
.use(router)
.mount('#app')
- 使用
<template>
<div @click="add">
{{count}}
</div>
</template>
<script setup>
import { computed } from 'vue'
import {useStore} from 'vuex'
let store = useStore()
let count = computed(()=>store.state.count)
function add(){
store.commit('add')
}
</script>
commit 对应 mutations
dispatch 对应 actions
手写vuex
import {inject,reactive} from 'vue'
//手写vuex
const STORE_KEY = '__store__'
function useStore(){
return inject(STORE_KEY) //获取vue中的对象
}
class Store{
constructor(options){
this.$options = options
this._state = reactive({
data:options.state()
})
this._mutations = options.mutations
}
get state(){
return this._state.data
}
commit = (type,payload) => {
const entry = this._mutations[type]
entry && entry(this.state,payload)
}
//app.use 时候会运行 直接注册到全局
install(app){
app.provide(STORE_KEY,this)
}
}
function createStore(options){
return new Store(options)
}
export {createStore,useStore}
11 vue-router
原理
1 hash url中带#号 hashChange 监听函数
window.addEventListener('hashchange',fn)
2 history 正常url popstate 监听函数
window.addEventListener('popstate', fn)
vue-router 中对应两个函数,分别是 createWebHashHistory 和 createWebHistory
2014年 HTML5标准 两个 API pushState replaceState 改变URL地址 并且浏览器不会向后端发送请求
原理演示版
- 目录
-src
-components
cat.vue
dog.vue
- router
index.js
-grouter
index.js
RouterLink.vue
RouterView.vue
app.vue
main.js
-
demo组件
//cat.vue <template> <h1 style="color:red"> this is cat </h1> </template>doc.vue <template> <h1 style="color:blue"> this is dog </h1> </template> -
router组件编写
//grouter/index.js import {ref,inject} from 'vue' const ROUTER_KEY = '__router__' import RouterLink from './RouterLink.vue' import RouterView from './RouterView.vue' function createWebHashHistory(){ function bindEvents(fn){ window.addEventListener('hashchange',fn) } return{ bindEvents, url: '/' } } class Router{ constructor(options){ this.history = options.history //createWebHashHistory {bindEvents: url:} this.routes = options.routes this.current = ref(this.history.url) //关键 current为响应式变量 this.history.bindEvents(()=>{ //监听路径改变 改变current值 this.current.value = window.location.hash.slice(1) }) } install(app){ app.provide(ROUTER_KEY,this) //注册实例 只有一个 app.component("router-link",RouterLink) //注册组件 可以重复使用 app.component("router-view",RouterView) } } // function createRouter(options){ return new Router(options) } // function useRouter(){ return inject(ROUTER_KEY) } export {createRouter,createWebHashHistory,useRouter}1 window.addEventListener 绑定 hash变化事件函数
2 hash变化后把当前hash值赋值给router中的响应对象 current
3 routerView组件监听current
3 routerView.vue
//RouterView.vue
<template>
<component :is="comp">
</component>
</template>
<script setup>
import {computed} from 'vue'
import {useRouter} from '../grouter/index'
let router = useRouter()
const comp = computed(()=>{
const route = router.routes.find(
(route) =>return route.path === router.current.value
)
return route?route.component:null
})
</script>
通过computed监听router.current,通过component内置组件进行自定义组件(cat.vue,dog.vue)显示
- RouterLink.vue
// RouterLink.vue
<template>
<a :href="'#'+props.to">
<slot />
</a>
</template>
<script setup>
import { defineProps } from 'vue';
let props = defineProps({
to:{type:String,required:true}
})
</script>
改变hash值 自动触发绑定的hash事件函数
- 路由配置
//router/index.js
import{
createRouter,
createWebHashHistory,
} from './grouter/index'
import dog from '../components/dog.vue'
import cat from '../components/cat.vue'
export default createRouter({
history:createWebHashHistory(),
routes:[{
path:"/dog",
component:dog,
},{
path:"/cat",
component:cat,
}
]
})
- app.vue
//app.vue
<template>
<router-link to="/dog">
DOG
</router-link>
<router-link to="/cat">
CAT
</router-link>
<router-view></router-view>
</template>
- main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')