npm init vite@latest my-project
cd my-project
npm install
npm run dev
npm install vue-router@next --save
在src目录下创建router文件夹,在文件夹下创建index.ts
import { createRouter,Router, createWebHistory, RouteRecordRaw } from 'vue-router';
const history = createWebHistory()
const routes: Array = [
{
path: '/',
name: '/index',
component: () => import('../views/Index.vue'),
meta:{
title:'首页'
}
},
];
const router: Router = createRouter({
history:createWebHistory('/'),
routes,
})
export default router
在main.ts中引入router
import router from './router/index'
createApp(App).use(router).mount('#app')
将App.vue中内容替换
npm install --save-dev sass
就这么一句安装就可以使用了,用vuecli的时候,还要安装sass-loader、node-sass等,但是vite只需要安装sass就可以了
npm install pinia@next
数据持久化,可使用插件pinia-plugin-persist
npm install pinia-plugin-persist -S
import {createPinia} from "pinia"
//需要注意的是从pinia中结构出来的createPinia是一个函数,挂载需要先调用执行
import piniaPersist from 'pinia-plugin-persist'
const store =createPinia()
store.use(piniaPersist)
app.use(createPinia())
// index.js
import {defineStore} from "pinia"
export const useAppStore = defineStore("app",{
state:()=>{
return{
count:1,
number:1
}
},
getters:{
bdCount(){
return this.count*2
}
},
actions:{
increment(num){
this.count=Math.round(100 * Math.random()*num)
}
},
persist: {
enabled: true,
//通过修改配置项,改成localStorage
strategies: [
{
storage: localStorage,
//paths: ["count"], //可以通过paths字段指定需要缓存的数据,如不指定paths,则默认是缓存所有
},
],
}
})
pinia使用
我是index里面的值:{{count}}
我是getter里面的值:{{bdCount}}
安装axios,然后main.ts文件挂载axios
npm install axios
import Axios from 'axios'
createApp( App ).config.globalProperties.Axios = Axios
创建utils目录并封装request.ts
import axios, {AxiosInstance, AxiosRequestConfig} from 'axios'
import { $serverUrl } from "../config"
class HttpRequest {
private readonly baseUrl: string;
constructor() {
this.baseUrl = $serverUrl
}
getInsideConfig() {
const config = {
baseURL: this.baseUrl,
headers: {
//
}
}
return config
}
// 请求拦截
interceptors(instance: AxiosInstance, url: string | number | undefined) {
instance.interceptors.request.use(config => {
// 添加全局的loading..
// 请求头携带token
return config
}, (error: any) => {
return Promise.reject(error)
})
//响应拦截
instance.interceptors.response.use(res => {
//返回数据
const {data} = res
return data
}, (error: any) => {
console.log('error==>',error)
return Promise.reject(error)
})
}
request(options: AxiosRequestConfig) {
const instance = axios.create()
options = Object.assign(this.getInsideConfig(), options)
this.interceptors(instance, options.url)
return instance(options)
}
}
const http = new HttpRequest()
export default http
创建apis目录编写请求文件
import http from '../utils/request'
export function getFileList() {
return http.request({
url: '/lencon/pc/index/school_type',
method: 'post'
})
}
export function saveSysEventType (parameter:any) {
return http.request({
url: '/lencon/pc/index/titleImg',
method: 'post',
data: parameter,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
页面请求编写
在src根目录下创建一个后缀为env.d.ts的文件
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
declare module '@/store/index.js'
完整vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
//npm install @types/node --save-dev
import path from 'path'
//vite打包性能优化之开启Gzip压缩
import viteCompression from "vite-plugin-compression"; //npm i vite-plugin-compression -D
export default defineConfig({
server:{
host: '0.0.0.0',
port: 3000,
strictPort: false, // 若3000端口被占用,是否直接结束项目
https: false, // 是否开启 https
open: true, // 是否自动在浏览器打开
proxy: {
"/api": {
target: "http://pro.dangjian.link",
changeOrigin: true,
secure: false, // 如果是https接口,需要配置这个参数
// ws: true, // websocket是否支持
rewrite: (path) => path.replace(/^\/api/, ""),
},
}
},
build: {
target: "es2020", //指定es版本,浏览器的兼容性
outDir: "dist",
assetsDir: "assets", // 指定静态资源存放路径
// cssCodeSplit: true, // css代码拆分,禁用则所有样式保存在一个css里面
sourcemap: false, // 是否构建source map 文件
// manifest: true, // 是否在outDir中生成 manifest.json
rollupOptions: {
// input: '/path/to/main.ts' // 覆盖默认的 .html 入口
},
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
}
},
resolve: {
// 配置项目路径别名,在开发时不需要写完整的路径名称,需要安装 @types/node,执行命令npm i -D @types/node --save-dev即可
alias: {
"@": path.resolve(__dirname, "src"),
"@assets": path.resolve(__dirname, "src/assets"),
"@components": path.resolve(__dirname, "src/components"),
},
},
optimizeDeps: {
include: [],
},
plugins: [
vue(),
viteCompression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: "gzip",
ext: ".gz",
})
]
})
npm install animate.css --save
import 'animate.css';
Example
Animate.css官网
https://animate.style/
npm i hover.css --save
import 'hover.css'
Example
hover.css官网
http://ianlunn.github.io/Hover/
main.ts
const checkImg = (value:string) => {
if (!value) return ''
if(value.substring(0,4)!='http'){
return $serverUrl+value;
}else{
return value;
}
}
app.config.globalProperties.$checkImg = checkImg
组件内使用
import { getCurrentInstance } from 'vue'
const { $checkImg } = currentInstance.appContext.config.globalProperties
npm install element-plus --save
html 引入
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
安装icon
npm install @element-plus/icons-vue
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
按需导入需要再继续安装两个插件
npm install -D unplugin-vue-components unplugin-auto-import
在项目的vite.config.ts中引入并配置plugins
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default {
plugins: [
// ...
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
}
正常使用icon
import * as Icons from "@element-plus/icons-vue";
Object.keys(Icons).forEach((key) => {app.component(key, Icons[key as keyof typeof Icons]);});
按需引入icon
安装插件
npm install -D unplugin-auto-import unplugin-icons
main可以去掉全局引入
// import * as Icons from "@element-plus/icons-vue";
const app = createApp(App)
// Object.keys(Icons).forEach((key) => {app.component(key, Icons[key as keyof typeof Icons]);});
vite.config.ts中修改
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
plugins: [
vue(),
AutoImport({
// Auto import functions from Vue, e.g. ref, reactive, toRef...
// 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
imports: ['vue'],
// Auto import functions from Element Plus, e.g. ElMessage, ElMessageBox... (with style)
// 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
resolvers: [
ElementPlusResolver(),
// Auto import icon components
// 自动导入图标组件
IconsResolver({
prefix: 'Icon',
}),
],
dts: path.resolve(path.resolve(__dirname,'src'), 'auto-imports.d.ts'),
}),
Components({
resolvers: [
// Auto register icon components
// 自动注册图标组件
IconsResolver({
enabledCollections: ['ep'],
}),
// Auto register Element Plus components
// 自动导入 Element Plus 组件
ElementPlusResolver(),
],
dts: path.resolve(path.resolve(__dirname,'src'), 'components.d.ts'),
}),
Icons({
autoInstall: true,
}),
// AutoImport({
// resolvers: [ElementPlusResolver()],
// }),
// Components({
// resolvers: [ElementPlusResolver()],
// }),
vueSetupExtend (),
]
页面使用
npm i --save ant-design-vue
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
const app = createApp(App);
app.use(Antd).mount('#app');
如果你使用的 Vite,你可以使用 unplugin-vue-components 来进行按需加载。但是此插件无法处理非组件模块,如 message,这种组件需要手动加载:
import { message } from 'ant-design-vue';
import 'ant-design-vue/es/message/style/css'; //vite只能用 ant-design-vue/es 而非 ant-design-vue/lib
npm install swiper@6 --save
import SwiperCore, {Autoplay,Pagination} from 'swiper';
import {Swiper,SwiperSlide} from 'swiper/vue';
import 'swiper/swiper.min.css';
import 'swiper/components/pagination/pagination.scss';
SwiperCore.use([Autoplay,Pagination]);
export default {
components: {
Vue3SeamlessScroll,Swiper, SwiperSlide
},
setup () {
const store = useStore()
let swiper_options = reactive({
autoplay:{
delay:8000,
disableOnInteraction: true
},
loop:true,
speed:1000,
pagination:{
clickable: true
}
})
const data = reactive({
tableData:[],
page:1,
totalPages:1,
deptId: computed(() => {
return store.getters.deptId
}),
async getData () {
let { deptId } = data
let res = await Axios.post("/lencon/bigData/learning/problem", { deptId,page:data.page,pageSize:5 })
if ( res.data.code == 1 ) {
data.tableData = res.data.res
data.totalPages= res.data.totalPages
}
},
})
const onSwiper = (swiper:any) => {
// if(data.page==data.totalPages){
// data.page=1
// }else{
// data.page+=1
// }
// data.getData()
};
const onSlideChange = () => {
if(data.page==data.totalPages){
data.page=1
}else{
data.page+=1
}
data.getData()
};
watch(() => data.deptId, () => {
data.getData()
})
// 初始化表格
onMounted(() => {
data.getData()
})
return {
...toRefs( data ),
swiper_options,onSwiper,onSlideChange
}
}
10.2 Vue3SeamlessScroll无缝滚动
npm install vue3-seamless-scroll --save
import vue3SeamlessScroll from 'vue3-seamless-scroll';
const app = createApp(App);
app.use(vue3SeamlessScroll)
https://www.npmjs.com/package/vue3-seamless-scroll
11.集成echarts
npm i echarts --save
使用 可以采用这位大佬的笔记 懒得写了 哈哈
https://blog.csdn.net/sunddy_x/article/details/124432548
补充 vite 引入文件
{ iconClass: new URL('@/assets/images/index/index_right_2.png', import.meta.url).href}
12. vite-plugin-vue-setup-extend此插件解决了:使用setup语法糖的时候没办法直接为组件定义name(页面缓存需要name属性)
npm i vite-plugin-vue-setup-extend -D
在vite.config.ts配置
import vueSetupExtend from 'vite-plugin-vue-setup-extend'
plugins: [
vueSetupExtend ()
]
完整的main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
import Login from '@/components/common/Login.vue'
import ElementPlus from 'element-plus'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import 'element-plus/dist/index.css'
import 'animate.css'
import 'hover.css'
import {createPinia} from "pinia"
import piniaPersist from 'pinia-plugin-persist'
const store =createPinia()
store.use(piniaPersist)
import Axios from 'axios'
import { $serverUrl } from "./config"
const checkImg = (value:string) => {
if (!value) return ''
if(value.substring(0,4)!='http'){
return $serverUrl+value;
}else{
return value;
}
}
const app = createApp(App)
app.config.globalProperties.$serverUrl = $serverUrl
app.config.globalProperties.Axios = Axios
app.config.globalProperties.$checkImg = checkImg
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.component('Login',Login).use(ElementPlus).use(router).use(store).mount('#app')
13.引入插件时候报错:无法找到模块“pinia”的声明文件等。
可能是TS升级到5.x带来的规范性问题
把tsconfig.json配置项moduleResolution:"bundler"改为 moduleResolution:"node"就可以了
("moduleResolution": "node" 表示 模块化查找的时候按照nodejs方式进行查找。"moduleResolution": "bundler" 表示 打包工具的模块解析策略来查找。 TS升级到5.x,默认"moduleResolution": "bundler" )
————————————————
版权声明:本文为CSDN博主「shuijiao123456」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/shuijiao123456/article/details/130498486
二、组件通讯
父子向子组件传值
父组件
子组件
defineProps<{
jsonData:any
}>();
子组件向父组件传值
子组件
const emit = defineEmits(['addclick','removeclick','editclick'])
const addFather = ()=>{
emit('addclick', {name: "干部培训666",type:22} )
}
const removeFather = (e:String,index:number)=>{
emit('removeclick', {id:e,type:index} )
}
const editFather = (e:object,index:number)=>{
emit('editclick', {...e,index:index} )
}
父组件
const addclick = (obj:object)=>{
data.dirct.push(obj)
}
const removeclick = (e:any)=>{
data.dirct.splice(e.type,1)
}
const editclick = (e:any)=>{
data.dirct[e.index].name='干部培训a:'+e.type
}
父组件获取子组件内的值及修改子组件的值
父组件
我是子组件内的值:{{data.sondata}}
获取子组件值
let navbarheader = ref()
const getSonData= ()=>{
data.sondata=navbarheader.value.sonName
}
const editSonData= ()=>{
navbarheader.value.editSon(5)
}
子组件(切记一定要defineExpose出来)
const counts = ref(0)
const editSon = (e:number)=>{
counts.value+=e
}
const sonName = ref("JackSon");
defineExpose({
sonName,
editSon
})
完整代码
父组件
我是子组件内的值:{{data.sondata}}
获取子组件值
子组件
{{item.name}}
修改父组件的值
删除父组件的值
增加父组件的值
补充(兄弟组件传值)
1.安装mitt
npm install --save mitt
2.创建一个js文件 比如 utils/bus.ts
import mitt from 'mitt'
const emiiter = mitt()
export default emiiter
3.组件中使用
组件A传值
import emitter from '../../utils/bus'
emitter.emit('handleEvent', ref({show:true}))
组件B收值
import { ref, reactive, onMounted, computed, watch, onBeforeMount, onUnmounted } from 'vue'
import emitter from '../../utils/bus'
onBeforeMount(() => {
// 开启监听,监听到handleEvent事件后,执行handleEvent函数
emitter.on('handleEvent',handleEvent)
})
const handleEvent = (Aname:any)=>{
navData.NeedLogin = Aname.value.show
}
onUnmounted(() => {
emitter.off('handleEvent',handleEvent)// 取消handleEvent事件的handelEventFn函数监听
emitter.off('handleEvent')// 取消handleEvent事件的全部处理函数的监听
})
多层组件传值(Provide / Inject)
import { provide, ref } from'vue'
let flag = ref(1)
provide('flag', flag)
子组件接收
import { inject, Ref, ref } from 'vue'
const flag = inject>('flag', ref(1))
修改值
const change = () => {
flag.value = 2
}
三、计算属性
购物车案例
Edit
Delete
合计:{{total}}
合计:{{token}}
pinia设置token
pinia清除token
四、监听属性watch和watchEffect
watch
watch(token,(newVal,oldVal)=>{
console.log('新的值----', newVal);
console.log('旧的值----', oldVal);
},{
immediate:true,
deep:true
})
watch([total,token],(newVal,oldVal)=>{
console.log('新的值----', newVal);
console.log('旧的值----', oldVal);
},{
immediate:true
})
immediate的值为true时表示非惰性的立即执行的(默认情况下是false)
deep深层次触发(此处设置deep无意义)
监视所定义的响应式数据和多个响应式数据
监听reactive对象
1.若监视的是reactive定义的响应式数据,则无法正确获得oldValue
2.若监视的是reactive定义的响应式数据,则watch会强制开启深度监视
let person = reactive({
name:'张三',
age:'18',
job:{
j1:{
salary:20
}
}
})
watch(()=>person.name,(newValue,oldValue)=>{
console.log('person的job改变了',newValue,oldValue)
})
watch(()=>person.age,(newValue,oldValue)=>{
console.log('person的job改变了',newValue,oldValue)
})
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job改变了',newValue,oldValue)
})
//从上边我们发现改变name,age都会触发监听,但是改变job不会
//这是因为name和age属性的值只是一个简单的基本类型数据,
//而job属性的值是一个对象,比较深,想要监视到,就要开启深度监视,程序如下:
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job改变了',newValue,oldValue)
},{deep:true})//此时job改变,会被监视到,此处的deep配置生效
//需要和情况三进行区分,此处watch监视的是reactive所定义的对象中的某个属性,而情况三watch监视的是reactive所定义的对象
//情况五:监视reactive所定义的响应式数据中的某些属性,写成数组的形式
watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
console.log('person的name或age改变了',newValue,oldValue)
})
watchEffect立即执行,没有惰性
watchEffect函数内部所指定的回调中用到的数据只要发生变化,就会重新执行回调
watchEffect(()=>{
console.log('新的值----', token.value);
})
对比
是否有惰性
参数
获得值
watch
有惰性,数值再次改变后执行监听函数
可以侦听多个数据的变化
参数可以拿到当前值和原始值
watchEffect
立即执行没有惰性
不需要传递侦听内容,自动感知代码依赖
不需要传递到很多参数,不能获取原始值
五、Vue3生命周期
onBeforeMount()
在组件DOM实际渲染安装之前调用。在这一步中,根元素还不存在。
onMounted()
在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问
onBeforeUpdate()
数据更新时调用,发生在虚拟 DOM 打补丁之前。
onUpdated()
DOM更新后,updated的方法即会调用。
onBeforeUnmount()
在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
onUnmounted()
卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
六、全局组件,递归组件,动态组件
全局组件的使用
递归组件的使用
父组件
子组件
{{ item.name }}
动态组件
-
{{ item.name }}
知识点补充 markRaw 和 toRaw
七、插槽
可以使用v-slot: 或者简写为# 进行插槽
1
2
3
作用域插槽
{{ data }}
我是动态插槽
作用域插值
动态插槽
我是动态插槽
八、路由跳转及路由传参
路由跳转
router.ts
{
path: '/newsDetail/:id',
name: '/newsDetail',
component: () => import('@/view/NewsDetail.vue'),
},
router.push({
path: `/newsDetail/${item.news_id}`,
query: {
cateName: props.cateName
}
})
路由传参
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
const route = useRoute()
console.log(route.query.cateName)
console.log(route.params.id)
九、keep-alive
开启keep-alive 生命周期的变化
初次进入时: onMounted> onActivated
退出后触发 deactivated
再次进入:
只会触发 onActivated
事件挂载的方法等,只执行一次的放在 onMounted中;组件每次进去执行的方法放在 onActivated中
十、Vue3.x中的自定义hook函数是什么?
使用Vue3的组合API封装的可复用的功能函数
自定义hook的作用类似于vue2中的mixin技术
自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂.
mixins的缺点:
1.变量来源不明确(隐式传入),不利于阅读,使代码变得难以维护。
2.多个mixins的生命周期会融合到一起运行,但是同名属性、同名方法无法融合,可能会导致冲突。
3.mixins和组件可能出现多对多的关系,复杂度较高(即一个组件可以引用多个mixins,一个mixins也可以被多个组件引用)。
补充知识点
declare 是声明的关键字,通常用在编写声明文件,你可以通过 declare 去声明一个变量、声明一个函数等等,一旦声明了,TypeScript 编译器就知道它的存在,你就可以在任何地方使用了。
declare type AsideState = {
menuList: RouteRecordRaw[];
clientWidth: number;
};
// columnsAside
declare type ColumnsAsideState = {
columnsAsideList: T[];
liIndex: number;
liOldIndex: null | number;
liHoverIndex: null | number;
liOldPath: null | string;
difference: number;
routeSplit: string[];
};
const state = reactive({
menuList: [],
clientWidth: 0,
});
十一、Teleport将其插槽内容渲染到 DOM 中的另一个位置
test
测试teleport
关闭teleport
十二、vue3集成使用echarts
1.安装echarts
npm install echarts --save
2.在页面引入
import * as echarts from "echarts";
3.初始化方法
(类型“HTMLElement | null”的参数不能赋给类型“HTMLElement”的参数。 不能将类)
var myChart = echarts.init(document.getElementById('allmetting') as HTMLElement);
另外:vite.config.ts的打包优化请移步:Vite项目打包优化
你可能感兴趣的:(vue,vue.js,前端,vue3,vite,typescript)