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@latest
cd <your-project-name>
npm install
npm run dev

or you can combine these two lines

npm i && npm run dev

these 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> tag

this 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 pinia

import './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 --save
import 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 init

see 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/' : '/',
})
  1. 当处于生产环境(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) 寻找资源。
  1. 当处于本地开发环境(如 development)时:
  • 条件判断:process.env.NODE_ENV === 'production' 为 false。
  • 应用路径:'/'
  • 实际效果:在本地启动开发服务器(如执行 npm run dev)时,资源直接从本地服务器的根目录(例如 http://localhost:5173/)加载,方便开发和调试。