CDN
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>Vue 3 脚手架
安装步骤:https://cn.vuejs.org/guide/quick-start.html
npm create vue@latestcd <your-project-name>
npm install
npm run devor you can combine these two lines
npm i && npm run devthese basically tell the terminal to perform the commands one after another.
Vue2
响应式
<div id="app">
<p>{{title}}</p>
<p>{{content}}</p>
</div>
<script>
const vm = new Vue({
// 响应式数据与插值方式
el: '#app',
data(){
return{
title: '这是标题文本',
content: '这是内容文本'
}
},
})
</script>也可通过Vue的示例vm直接访问 title 和 content(vm.title and vm.content)
methods attribute
通过methods属性,来响应式方法。(用于声明要混入到组件实例中的方法。)
<div id="app">
<p>{{ output() }}</p>
</div>
<script>
const vm = new Vue({
// 响应式数据与插值方式
el: '#app',
data(){
return{
title: '这是标题文本',
content: '这是内容文本'
}
},
// 函数调用
methods: {
output(){
return 'the output is:'+this.title + ','+this.content
}
}
})
</script>computed attribute
上面的例子中:
methods: {
output(){
console.log('method was performed')
return 'the output is:'+this.title + ','+this.content
}
}如果有多次混入
<p>{{ output() }}</p>
<p>{{ output() }}</p>
<p>{{ output() }}</p>method was performed会输出很多次,性能不好。
computed:{
output(){
console.log('method was performed')
return 'the output is:'+this.title + ','+this.content
}
}<p>{{ output }}</p>
<p>{{ output }}</p>
<p>{{ output }}</p>改成这样就只会执行一次,所有具有缓存性
指令
v-text and v-html
<div id="app">
<p v-text="htmlContent"></p>
<p v-html="htmlContent"></p>
</div>
<script>
const vm = new Vue({
// 响应式数据与插值方式
el: '#app',
data(){
return{
htmlContent: 'this is the <span>span</span> tag'
}
}
</script>this is the
<span>span</span>tagthis is the span tag
生成重复内容 for
<p v-for="item in 5">这是重复的内容</p>这是重复的内容
这是重复的内容
这是重复的内容
这是重复的内容
这是重复的内容
<p v-for="(item, key) in tesArr">这是重复的内容:{{item}};{{key}}</p> const vm = new Vue({
// 响应式数据与插值方式
el: '#app',
data(){
return{
tesArr:['a','b','c'],
}
}
}) 这是重复的内容:a;0
这是重复的内容:b;1
这是重复的内容:c;2
v-if
销毁内容,注意这里是直接销毁
<p v-if="false">内容}</p>隐藏内容
<p v-show="true">内容}</p>属性指令

<div id="titleDiv" title="this is the title of this div"></div>
添加动态绑定
<div id="titleDiv" :title="title"></div>
<div id="titleDiv" v-bind:title="title"></div> const vm = new Vue({
// 响应式数据与插值方式
el: '#app',
data(){
return{
title: '这是标题文本'
}
}
}) 事件指令
<div id="titleDiv" v-on:click="output"></div>
<div id="titleDiv" @click="output"></div>const vm = new Vue({
// 响应式数据与插值方式
el: '#app',
data(){
return{
title: '这是标题文本',
content: '这是内容文本',
}
},
// 函数调用
methods: {
output(){
console.log('method was performed')
return 'the output is:'+this.title + ','+this.content
}
},
}) 现在单击div,就会触发output函数
const vm = new Vue({
// 响应式数据与插值方式
el: '#app',
data(){
return{
title: '这是标题文本',
content: '这是内容文本',
}
},
// 函数调用
methods: {
output(){
console.log('method was performed')
return 'the output is:'+this.title + ','+this.content
}
},
}) 表单指令
<input type="text" v-model="inputValue">
<p>{{inputValue}}</p>const vm = new Vue({
el: '#app',
data(){
return{
inputValue: 'default input value'
}
}
}) 更改表单里的内容,p中的内容也会改变,双向数据绑定。
Vue3
Hello World
回顾解构语法:
const person = {
name: "小明",
age: 18
}
const { name: userName, age } = person //使用冒号来重命名结构的属性
console.log(userName, age)const person = {
name: "小明",
sayHello() {
console.log("你好")
}
}
const { sayHello } = person // 解构方法
sayHello() // 你好
// 也可以对方法重命名:
// 解构方法并重命名
const { sayHello: hello } = person
hello() // 你好匿名对象(object literal,对象字面量)的语法
function printUser(user) {
console.log(user.name)
}
printUser({
name: "小明",
age: 18
}) // 小明
<div id="app">{{ message }}</div>
<script>
const { createApp, ref } = Vue // 从Vue对象中解构出两个属性
createApp({
setup() {
const message = ref('Hello vue!')
return {
message
// 这里return后{}包裹的是返回一个对象
}
}
}).mount('#app') // CSS 选择器
</script>directory 结构
‘Under the hood’ generally refers to examining or understanding the internal workings of something, like a vehicle or a system, beyond just its surface appearance. 也就是底层
Under the hood, create VUE leverages Vit, a, fast and modern build tool to power both the development server and the, production build.,
vue-project/
├── public/ # 静态资源,直接复制到构建输出目录
│ └── favicon.ico # 网站图标
├── src/
│ ├── assets/ # 静态资源(如图片、字体、CSS)
│ │ └── logo.png
│ ├── components/ # 可复用的通用组件
│ │ └── CommonHeader.vue
│ ├── composables/ # 可复用的 Composition API 逻辑(可选)
│ │ └── useFetch.js
│ ├── router/ # 路由配置
│ │ └── index.js
│ ├── stores/ # Pinia 状态管理(或 store/ 用于 Vuex)
│ │ └── index.js
│ ├── utils/ # 工具函数(如请求封装、格式化)
│ │ └── request.js
│ ├── views/ # 页面级组件(对应路由页面)
│ │ ├── HomeView.vue
│ │ └── AboutView.vue
│ ├── App.vue # 根组件
│ └── main.js # 入口文件(创建 Vue 应用)
├── index.html # HTML 入口模板
├── vite.config.js # Vite 配置文件
├── package.json # 项目依赖与脚本
├── .gitignore # Git 忽略文件
├── .env # 环境变量(开发)
├── .env.production # 环境变量(生产)
└── README.md # 项目说明
a small example:
<script setup>
import { reactive } from 'vue';
const web = reactive({
show: true // 创建了一个响应式对象 web,里面有一个属性
})
const toggle = ()=>{
web.show = !web.show
}
</script>
<template>
<h3>state of web.show: {{ web.show }}</h3>
<p v-show="web.show">受 web.show 影响的内容</p>
<button @click="toggle">click to change the display of the tag above </button>
</template>
<style scoped>
</style>双向绑定
https://cn.vuejs.org/guide/essentials/reactivity-fundamentals.html
通过 Vue 3 脚手架创建好项目后,在App.vue中写入以下代码
<script>
import { ref } from 'vue'
export default {
// `setup` 是一个特殊的钩子,专门用于组合式 API。
setup() {
const count = ref(0)
// 将 ref 暴露给模板
return {
count
}
}
}
</script>
<template>
<div>{{ count }}</div>
</template>
<style scoped>
</style>使用 <script setup> 来大幅度地简化代码:
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
{{ count }}
</button>
</template>简单例子:
<script setup>
import { ref } from 'vue'
const userInfo = ref({
age: 0,
sex: 1,
department: '',
skills: ['java','python','vue']
})
</script>
<template>
<h2>Personal Info:</h2>
<div>
<p>age: <input type="number" v-model="userInfo.age"></p>
<p>sex:
<input type="radio" value="1" v-model="userInfo.sex">man
<input type="radio" value="2" v-model="userInfo.sex">woman
</p>
<p>
<select name="" id="" v-model="userInfo.department">
<option value="develop">develop</option>
<option value="test">test</option>
<option value="operations">operations </option>
</select>
</p>
<p>information summary:</p>
<p> {{userInfo}} </p>
</div>
</template>
<style scoped>
</style>ref 函数绑定
<script setup>
import {ref} from 'vue';
const userName = ref('roy') // 转成一个对象
const salary = ref(15000)
function addSalary(){
salary.value += 1000 // 通过对象的属性value来取出值
console.log(salary)
}
</script>
<template>
<div>
name:<input v-model="userName">{{ userName }}<br/>
salary: <input type="number" v-model="salary"> {{ salary }}<br/>
<button @click='addSalary'>increaseSalary</button>
</div>
</template>通过 ref,可以绑定普通类型。
也可以通过 reactive() 绑定对象,这是另一种声明响应式状态的方式。
<script setup>
import {reactive} from 'vue';
let salaryInfo = reactive({
userName: 'Alex',
salary: 1000
})
function addSalary(){
salaryInfo.salary += 100
}
</script>
<template>
<div>
name:<input v-model="salaryInfo.userName">{{ salaryInfo.userName }}<br/>
salary: <input type="number" v-model="salaryInfo.salary"> {{ salaryInfo.salary }}<br/>
<button @click='addSalary'>increaseSalary</button>
</div>
</template>
<style scoped>
</style>ref 也可以实现上面的效果,但是还要用 value 属性。
提取对象的属性,双向绑定
<script setup>
import {reactive, toRef} from 'vue';
let salaryInfo = reactive({
userName: 'Alex',
salary: 1000
})
// 提取对象的属性,双向绑定
let extractedUserName = toRef(salaryInfo, 'userName')
let extractedSalary = toRef(salaryInfo.salary)
function addSalary(){
extractedSalary.value += 100
}
</script>
<template>
<div>
name:<input v-model="extractedUserName">{{ extractedUserName }}<br/>
salary: <input type="number" v-model="extractedSalary"> {{ extractedSalary }}<br/>
<button @click='addSalary'>increaseSalary</button>
</div>
</template>或者用toRefs()
<script setup>
import {reactive, toRef, toRefs} from 'vue';
let salaryInfo = reactive({
userName: 'Alex',
salary: 1000
})
// 提取对象的属性,双向绑定
// let extractedUserName = toRef(salaryInfo, 'userName')
// let extractedSalary = toRef(salaryInfo.salary)
let {userName, salary} = toRefs(salaryInfo)
function addSalary(){
userName.value += 100
}
</script>子组件传父组件
directory info:
├── components/ # 可复用的通用组件
│ └── CommonHeader.vue
├── App.vue # 根组件
MySalaryInfo.vue
<template>
<div>
<h3>子组件</h3>
<p>姓名:{{ userName }}</p>
<p>工资:{{ salary }}</p>
<button @click="addSalary">涨工资</button>
<button @click="showProperties">showProperties</button>
</div>
</template>
<script setup>
import { reactive, toRefs } from 'vue'
const salaryInfo = reactive({
userName: 'Alex',
salary: 1000
})
const { userName, salary } = toRefs(salaryInfo)
function addSalary() {
salary.value += 100
}
function showProperties(){
console.log(userName) // ObjectRefImpl
}
/*
暴露给父组件
*/
defineExpose({
userName,
salary,
addSalary
})
</script>app.vue
<template>
<div>
<MySalaryInfo ref="salaryRef" />
<hr>
<h1> AppVue 父组件</h1>
<button @click="showData">
获取子组件数据
</button>
<button @click="parentAddSalary">
父组件调用子组件方法
</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import MySalaryInfo from './components/MySalaryInfo.vue'
/*
获取子组件实例
*/
const salaryRef = ref()
// 通过 ref 指令
// 获取 <MySalaryInfo ref="salaryRef" />里的salaryRef
// 子组件定义了defineExpose
function showData() {
console.log(salaryRef) // ref
console.log(salaryRef.value) // proxy
console.log('salaryRef.value.userName:'+salaryRef.value.userName)
console.log('salaryRef.value.salary:'+salaryRef.value.salary)
}
function parentAddSalary() {
salaryRef.value.addSalary()
}
</script>
<style scoped>
</style>
父传子
父组件app.vue
<template>
<Header propsName="Tom" propsUrl="https://example.com"></Header>
testlink...
<Footer></Footer>
</template>
<script setup>
import Header from './components/Header.vue';
import Footer from './components/Footer.vue';
</script>子组件 Header.vue
<script setup>
const props = defineProps(['propsName','propsUrl'])
</script>
<template>
<h1>This is Header</h1>
<p>
{{ props.propsName }}<br>
{{ props.propsUrl }}
</p>
</template>
另一种方式(通过v-bind 传递对象)
app.vue
<template>
<Header propsName="Tom" propsUrl="https://example.com"></Header>
testlink...
<Footer v-bind="propsWeb"></Footer>
</template>
<script setup>
import Header from './components/Header.vue';
import Footer from './components/Footer.vue';
const propsWeb ={
user: 'tom',
url: 'www.baidu.com'
}
</script>Footer.vue
<script setup>
const props = defineProps({
user: String,
url: String
})
</script>
<template>
<h1>This is Footer</h1>
<p>
content: <br>
{{ props.user }}<br>
{{ props.url }}
</p>
</template>
在接受数据时,可以设置默认值,和默认类型
const props = defineProps({
user: String,
url: {
type: String,
required: false, // 申明为不是必传项,改成true就是必传,不传报错
default: 'www.example.com' // // 没传就用我,安全可靠
}
})现在删掉App.vue中对象的url属性,也有默认值
const propsWeb ={
user: 'tom',
}
插槽
https://element-plus.org/zh-CN/component/input
<el-input
v-model="input"
class="w-60"
placeholder="Please input"
>
<template #append>
<el-icon>
<Search />
</el-icon>
</template>
</el-input>https://cn.vuejs.org/guide/components/slots.html#slots
导入 conponent typescript识别不出来
import Header from './components/Header.vue';by Agent:
解决,在tsconfig.json中添加以下内容:
或者在它的子目录env.d.ts中
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}Vue Router
https://router.vuejs.org/zh/installation.html
创建好vue项目,手动再添加 vue-router
npm install vue-router@4演示效果:
在src/目录下新建pages,里面创建三个文件
‘./pages/
HomePage.vue'
'./pages/NewsPage.vue'
'./pages/AboutPage.vue’
<template>
This is HomePage
</template>
<script setup>
</script>
<style scoped>
</style><template>
This is AboutPage
</template>
<script setup>
</script>
<style scoped>
</style><template>
This is NewsPage
</template>
<script setup>
</script>
<style scoped>
</style>参照文档,在main.js中添加router
// 引入 vue-router
import {createRouter, createWebHistory} from 'vue-router'
import HomePage from './pages/HomePage.vue'
import NewsPage from './pages/NewsPage.vue'
import AboutPage from './pages/AboutPage.vue'
// 1. 配置路由规则
const routes = [
{
path: '/home', // 路径
component: HomePage // 第二个参数为:/home/后面的页面
},
{
path: '/about', // 路径
component: AboutPage // 第二个参数为:/home/后面的页面
},
{
path: '/news', // 路径
component: NewsPage // 第二个参数为:/home/后面的页面
}
]
// 2. create a router
const router = createRouter({
history: createWebHistory(),
routes
})
// 3. 使用
let app = createApp(App)
app.use(router)
app.mount('#app')App.vue:
<template>
<p>
<RouterLink to="/home">首页</RouterLink>
<RouterLink to="/about">关于</RouterLink>
<RouterLink to="/news">news</RouterLink>
</p>
<div class="content">
<RouterView></RouterView>
</div>
</template>
<script setup>
</script>
<style scoped>
p{
display: flex;
width: 100%;
background-color: azure;
border: 1px dashed black;
}
.content{
border: 1px solid black;
width: 100%;
height: 200px;
}
</style>
现在就可以进行点击跳转了。
在reateRouter中,history参数的含义
const router = createRouter({
history: createWebHistory(),
routes
})在router配置中的history项为路由工作模式。Vue提供了两种工作模式:
- history模式
访问路径:URL不带#,斜杠链接,接近传统网站。缺点:容易产生404错误。
const router = createRouter({
history: createWebHistory(),
routes
})- hash模式
访问路径:URL带有#。缺点:对SEO不太友好。比较适合内部系统。
const router = createRouter({
history: createWebHashHistory(),
routes
})Pinia
https://pinia.vuejs.org/zh/introduction.html
Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。
install:
npm install piniaimport './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
// 引入 vue-router
import {createRouter, createWebHistory} from 'vue-router'
import HomePage from './pages/HomePage.vue'
import NewsPage from './pages/NewsPage.vue'
import AboutPage from './pages/AboutPage.vue'
// 1. 配置路由规则
const routes = [
{
path: '/home', // 路径
component: HomePage // 第二个参数为:/home/后面的页面
},
{
path: '/about', // 路径
component: AboutPage // 第二个参数为:/home/后面的页面
},
{
path: '/news', // 路径
component: NewsPage // 第二个参数为:/home/后面的页面
}
]
// 2. create a router
const router = createRouter({
history: createWebHistory(),
routes
})
// 3. 使用
let app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(router)
app.mount('#app')
Element-Plus
https://element-plus.org/zh-CN/guide/installation
npm install element-plus --saveimport ElementPlus from 'element-plus'
import 'element-plus/dist/index.css' // 默认样式
import 'element-plus/theme-chalk/dark/css-vars.css' // 白天/黑夜
app.use(ElementPlus)
// ------ 监听主题变化
const media = window.matchMedia('(prefers-color-scheme: dark)')
function updateTheme() {
if (media.matches) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
}
// 初始化
updateTheme()
// 监听系统变化
media.addEventListener('change', updateTheme)tailwindcss
(中文的已经过时了)
https://www.tailwindcss.cn/docs/installation
npm install -D tailwindcss
npx tailwindcss initsee the latest documentation:
https://tailwindcss.com/docs/installation/using-vite
Vite config
export default defineConfig({
plugins: [
vue(),
vueDevTools({
launchEditor: 'idea',
}),
tailwindcss()
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
base: process.env.NODE_ENV === 'production' ? '/separatedProjectBasedOnSession/' : '/',
})- 当处于生产环境(
production)时:
- 条件判断:
process.env.NODE_ENV === 'production'为true。 - 应用路径:
'/separatedProjectBasedOnSession/' - 实际效果:项目打包构建(如执行
npm run build)后,所有引用的静态资源(如<script src="...">或<link rel="...">)路径都会自动加上前缀/separatedProjectBasedOnSession/。 - 适用场景:二级目录(子路径)部署。 例如,你的域名是
[https://example.com](https://example.com),但这个项目并不是直接放在根域名下,而是放在[https://example.com/separatedProjectBasedOnSession/](https://example.com/separatedProjectBasedOnSession/)这个子路径下。如果不配这个base,浏览器就会去[https://example.com/js/app.js](https://example.com/js/app.js)找文件,导致报 404 错误;配置后,它会自动去[https://example.com/separatedProjectBasedOnSession/js/app.js](https://example.com/separatedProjectBasedOnSession/js/app.js)寻找资源。
- 当处于本地开发环境(如
development)时:
- 条件判断:
process.env.NODE_ENV === 'production'为false。 - 应用路径:
'/' - 实际效果:在本地启动开发服务器(如执行
npm run dev)时,资源直接从本地服务器的根目录(例如http://localhost:5173/)加载,方便开发和调试。