vue3开发笔记
这是一篇vue3的尝试笔记。简单的记录一下,如果使用vue3及vite工具,来构建单页的app。
资源
通用资源
前端ui组件
正文
vue学习路线
vue,1、首先它是个模板引擎,数据绑定到html中,text节点及属性节点。条件、循环、简单表达式等。2、数据变化后的跟踪能力,做到数据的双向绑定。3、抽象成组件的能力,复用。
对比之前学过的extjs,对比而言:extjs就想一个大型的超市一样,包罗万象,就是你需要的有,你不需要的东西也有,内容非常的丰富。但是呢,也带来非常大的复杂度,可能我的需求很简单,就想买一瓶矿泉水,但是,不得不逛整个超市,才能买到。extjs最终是一个成品,所有的ui都是定好的。如果类比,有点像,vue+element 的avue组件呢,通过简单的配置即可,即一切皆配置。对于使用着,只需要提供数据,并在合适的时机,进行初始化,以及响应事件。
学习extjs的感受是:1、一切皆配置,自己写的方法,在真正运行的时候,绑定到的是其他对象。2、在合适的时机,处理事件。比如,初始化。或者响应特定的事件。3、不同组件之间,抽象出一个组件,作为消息的bus,方便各个组件之间的状态进行传递。
vue应该也是差不多的使用方式。
vue嘛,什么是组件。我定义的一个普通对象,为啥就成了组件了?vue一定需要路由组件吗?extjs,及时没有路由,它也还是能做成单页的app,页面不跳转的。vue应该也是同类。各种组件组合在一起,extjs用到的是,布局layout,通过简单的配置,它会控制各类数据是否要显示,即使用户看不到,但是呢,不代表ui不存在,可能已经加载到内存里面。
既然是配置,this的指向问题。
vue的动态问题。
vue安装
假设是npm6版本
npm init vite@latest <project-name> --template vue
npm init vite@latest qcweb --template vue
cd qcweb
npm fund
npm install
npm run dev
打开浏览器,访问提示的地址,能看到demo效果。
NutUI
选择vue3方式。
#npm i @nutui/nutui
# Vue3 项目
npm i @nutui/nutui@next
npm install vite-plugin-style-import --save-dev
在vue.config中添加如下配置
import vue from '@vitejs/plugin-vue'
import styleImport from 'vite-plugin-style-import';
export default {
plugins: [
vue(),
styleImport({
libs: [
{
libraryName: '@nutui/nutui',
libraryNameChangeCase: 'pascalCase',
resolveStyle: (name) => {
return `@nutui/nutui/dist/packages/${name}/index.scss`
}
}
],
}),
],
css: {
preprocessorOptions: {
scss: {
// 配置 nutui 全局 scss 变量
additionalData: `@import "@nutui/nutui/dist/styles/variables.scss";`
}
}
}
};
vue.config最终的样子,大概如下:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import styleImport from 'vite-plugin-style-import';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
styleImport({
libs: [
{
libraryName: '@nutui/nutui',
libraryNameChangeCase: 'pascalCase',
resolveStyle: (name) => {
return `@nutui/nutui/dist/packages/${name}/index.scss`
}
}
],
}),
],
css: {
preprocessorOptions: {
scss: {
// 配置 nutui 全局 scss 变量
additionalData: `@import "@nutui/nutui/dist/styles/variables.scss";`
}
}
}
})
使用nutUI
在src\main.js中设置要引入的组件,然后在其他的任何组件内都可以使用了。(全局引入的方式)
import { createApp } from 'vue'
import App from './App.vue'
import { Button } from '@nutui/nutui';
// import "@nutui/nutui/dist/style.css";
createApp(App)
.use(Button)
.mount('#app')
router
https://www.jianshu.com/p/ca0615a8ce08
#npm install vue-router@4.0.0-beta.13
npm install vue-router@next
# 安装的版本如下
# "vue-router": "^4.0.12"
router测试
定义路由文件
src/router/index.js
注意事项:使用相对地址来引入路由文件。大概的格式如下:
import { createRouter,createWebHashHistory} from "vue-router";
const home = () => import("../components/Home.vue")
const about = () => import("../components/About.vue")
const routes = [
{ path: "/", redirect: "/home" },
{
path: "/home",
name: "home",
component: home
},
{
path: "/about",
name: "about",
component: about
}
]
const router = createRouter({
history: createWebHashHistory(),
routes: routes
})
export default router;
增加页面
home about(略)
<script setup>
</script>
<template>
<h1>home </h1>
<router-link to="about">进入about</router-link>
</template>
<style scoped>
</style>
增加路由入口
src/App.vue
<template>
<router-view></router-view>
</template>
引入路由文件
核心代码如下:
import router from './router'
createApp(App)
.use(router)
.mount('#app')
完整的代码如下:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { Button,Tabbar,TabbarItem} from '@nutui/nutui';
// import "@nutui/nutui/dist/style.css";
createApp(App)
.use(router)
.use(Button)
.use(Tabbar)
.use(TabbarItem)
.mount('#app')
router import
使用相对地址,来引入文件。可以实现按需加载。
const home = () => import("../components/Home.vue")
路由的常用方法
go(-1): 原页面表单中的内容会丢失;
this.$router.go(-1):后退+刷新;
this.$router.go(0):刷新;
this.$router.go(1) :前进
back(): 原页表表单中的内容会保留;
this.$router.back():后退 ;
this.$router.back(0) 刷新;
this.$router.back(1):前进
配置本地代理
https://blog.csdn.net/qq_36293096/article/details/118364950
另外,一个教程,配置不同。https://blog.csdn.net/thinklog2018/article/details/119813204
可以配置server,proxy,指定本地服务监听的配置,以及配置代理功能。
export default defineConfig({
plugins: [
vue(),
],
server: {
host: '0.0.0.0',
port: 10086, // 设置服务启动端口号
open: false, // 设置服务启动时是否自动打开浏览器
cors: true, // 允许跨域
// 设置代理,根据项目实际情况配置
proxy: {
'/api': {
target: 'http://localhost:8003/api/v1',
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace('/api/', '/')
}
}
}
})
axios
安装网络请求的库。官方文档
npm install axios
https://www.jianshu.com/p/363c96793d62
https://www.cnblogs.com/hailexuexi/p/14261530.html
axios.get('/api/web/index.php/v1/warning_order/list?order_id=' + 1).then(function(response){
console.log(response.data.data)
console.log(response.data.data.rows)
response.data.data.rows.forEach(function(item){
me.m_list.push(item)
})
//下面的获取不到数据
for(row in response.data.data.rows){
console.log(row);
}
})
card布局
大概的需求是card布局。如在card1操作后,再进入card2,从card2返回后,card1才操作应该还在,状态不丢失,及不应该重新刷新。
个人觉得以下几个方式都可以尝试。
v-show/v-if
即,将这几页的内容都放到一个组件中。通过控制各部分的显示、隐藏来实现。弊端,可能每次页面滚动的位置可能会变化。优点:不需要使用复杂的路由、也不需要父子组件传递消息。
代码示例。
data(){
return {
item:{},
page:1,
}
},
<div v-show="page==2">
<nut-navbar @on-click-back="page=1" @on-click-title="page=1" title="选择员工"></nut-navbar>
<div>
<div @click="(this.zhuanPai = 1) && (page =1)">选择我</div>
</div>
</div>
动态组件
使用动态组件的功能来实现。那必须要将两个页面设计成两个组件,所以呢,存在兄弟组件借助父子件,传递状态变化的问题。
<keep-alive>
<component :is="currentTabComponent"></component>
</keep-alive>
子路由
通过嵌套的子路由,在上一层的路由中,能保存状态,待探索。
常用绑定功能
常用功能简述。绑定的表达式,一般都是合法的js表达式,(所以,字符串就要用引号),既然是绑定,vue肯定知道你要绑定的是组件内的东西,如数据、方法、计算属性等。所以呢,组件内部的这些懂,统统不用再额外加上 this了。(设计者肯定也想到怎么简单,怎么来,this这块能省就省了)
简单的数据绑定
<div>{{item.rule_name}}</div>
使用js表达式
{{isValid?'提交':'请填写必填项后再提交'}}
绑定页面的html
<div v-html="content"></div>
输入必须要双向绑定 v-model
<input @change="selectUser" v-model="searchText">
绑定节点属性 v-bind: 能简写
:disabled="!isValid"
条件判断是否显示,类似的还有 v-if.
<div class="monitor-page" v-show="isHandle">
简单的事件,直接写在dom上
<div class="monitor-detail" @click="isVisible=true">
<div class="monitor-detail" v-show="handleType == 4" @click="page=2">
循环 + 事件,注意,事件内能直接使用item的属性 这就爽歪歪了
<div class="user-item" v-for="item in userList" v-bind:key="item.number" @click="(this.zhuanPai = item.number) && (page = 1) && (searchText == '')">{{item.name}}({{item.number}})</div>
父子组件,事件的配合。
子组件,点击的时候,触发remove事件,父组件,定义了remove事件的响应。
<ul>
<todo-item
v-for="(todo, index) in todos"
:key="todo.id"
:title="todo.title"
@remove="todos.splice(index, 1)"
></todo-item>
</ul>
app.component('todo-item', {
template: `
<li>
{{ title }}
<button @click="$emit('remove')">Remove</button>
</li>
`,
props: ['title'],
emits: ['remove']
})
vue3知识点梳理
简单理解,从viewModel->视图绑定数据。在此基础上,完成封装成组件的抽象,增加一些必要的抽象功能。
数据绑定:
文本数据,使用花括号来完成绑定。
属性的绑定,v-bind:属性名称=’表达式’。动态属性名,则[属性名称]
数据双向绑定,v-model,针对input
绑定支持变量、简单的表达式、三元运算符号等。
数据绑定升级:
因为class、style这些功能比较常用。故,增加了对象、数组功能。分开而言:
v-bind:class=”对象” 根据当对象的value为真时,则增加类名称name。
v-bind:class=”[]” 数组时,则绑定多个值。其内部,还可以嵌套:1、对象(对象的真假,决定是否有该值),2、三元运算符号。
除了能绑定html上的class、还能绑定组件。组件如果想要绑定到特定的节点上,则可以用$attr.class来指定。
样式绑定,也同样支持数组(多个对象组成的数组)、对象语法(将有原有的样式,变成js对象格式)。
注意点:由于样式中可能包含-,故如果原有写,则js对象需要用引号包裹,(变量名不能有-,但是作为字符串则可以有)。也可以用驼峰命名。(这在extjs中也是类似的)
模板语法:
v-if 条件渲染。惰性加载。会触发组建的重建等。
v-show 通过样式控制,是否显示。
事件
v-on:等同于@。但是事件,区分于是原生的dom事件,还是组件自定义的事件。两着略有不同。父子组件之间,事件也可以通过@来触发。子组件触发事件,父组件,监听事件。
下面方式1、方式2的对比,难道是说,如果你传入了一个函数,虽然没有调用,底层会帮忙调用。如果,已经调用了,则就不代劳了?那它是怎么区分,是否已经调用了?
方式1:不带参数
注意,原生事件event,虽然没有传,也能接受到。
<div id="event-with-method">
<!-- `greet` 是在下面定义的方法名 -->
<button @click="greet">Greet</button>
</div>
Vue.createApp({
data() {
return {
name: 'Vue.js'
}
},
methods: {
greet(event) {
// `methods` 内部的 `this` 指向当前活动实例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM event
if (event) {
alert(event.target.tagName)
}
}
}
}).mount('#event-with-method')
方式2:带参数。
<div id="inline-handler">
<button @click="say('hi')">Say hi</button>
<button @click="say('what')">Say what</button>
</div>
Vue.createApp({
methods: {
say(message) {
alert(message)
//居然在这个里面也能访问到event变量
}
}
}).mount('#inline-handler')
增加event参数,
<button @click="warn('Form cannot be submitted yet.', $event)">
接收的函数,签名符合要求即可。
多事件处理
事件修饰符
按键修饰符
表达单输入绑定
多行文本行,换行居然用一个样式代替了br标签
<p style="white-space: pre-line;">{{ message }}</p>
组件之间关系
app.component('blog-post', {
props: ['title'],
emits: ['enlargeText']
})
props 纽带,属性赋值。
事件
子组件,通过原生的dom事件,然后往上触发事件,父组件能定义监听的消息。完成父子组件之间的消息传递。
Vite配置文件设置别名
https://www.jianshu.com/p/1e177c2ccaa6
配置:
const { resolve } = require('path') //必须要引入resolve
export default {
alias: {
'/@/': resolve(__dirname, 'src') //把src改为@
}
}
使用:
import HelloWorld from '/@/components/HelloWorld.vue'
import '/@/assets/app.css'
配置资源访问的url的前缀
这样,生成的资源,都是以绝对路径引入的。
export default defineConfig({
base:'/frontend/web/gaojing/',
配置
https://blog.csdn.net/qq_41499782/article/details/111660366
https://www.jianshu.com/p/2c2d1a31f834
https://www.cnblogs.com/Man-Dream-Necessary/p/13725049.html
vue3在低版本安卓上无法运行。
https://segmentfault.com/q/1010000039123955
Vue3 的核心依赖于 ES6 的 Proxy 对象,而这个特性在 Chrome 49 开始才引入。
而 Android 5.0 内置的 Webview 版本是 Chromium 37……
除非是自己做 App 内嵌一个 X5 之类的浏览器引擎,否则系统原生 Webview 肯定是打不开的。
P.S. Chromium 和 Chrome 的关系不用解释吧?
P.S. ES6 虽然是 ES2015,但实际上在 2017 年之前,只有 Firefox 才完全支持 ES6 的特性。
问题记录
访问
The request url “C:\Users\user\Desktop\qcweb\src\main.js” is outside of Vite serving allow list.
后来才发现,换了一个端口。
Port 3000 is in use, trying another one...
Port 3001 is in use, trying another one...
Port 3002 is in use, trying another one...
端口占用一直变
https://blog.csdn.net/qq_36237149/article/details/106073096
找到了esbuild.exe一直启动。然后搜索这个工具的用法。
https://zhuanlan.zhihu.com/p/342336095
php -s模式代理无法使用
代理配置好了,但是,后端的服务是使用php -S 的方式运行的,一次只能连接一个客户端。然后使用vite配置好代理(配置都是正确的),却无法正常的将请求代理到后端,后经排查,直接代理到百度都是行得通的,最终确定,是因为php -S的后端问题。