vue3项目实战
-知乎日报第2天components目录中的组件
变成全局组件。也可以自己注册全局组件-更灵活,也可添加函数式调用的组件。v-if
与v-else
来区分,非一个独立的根节点的地方,用template标签
包起来。
{{ dayjs(item.date).format('MM月DD日') }}
小主,精彩数据准备中...
jsx语法
还是模板语法
。src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
import createPersistedState from 'pinia-plugin-persistedstate'
import global from './global'
// pinia持久化存储
const pinia = createPinia()
pinia.use(createPersistedState)
// 创建应用
const app = createApp(App)
app.use(router)
app.use(pinia)
app.use(global)
app.mount('#app')
src/global.js
import 'lib-flexible'
import { showToast, Lazyload } from 'vant'
import NewsItem from '@/components/NewsItem.vue'
import NavBack from '@/components/NavBack.vue'
// vant@4中函数组件的样式
import 'vant/es/toast/style'
import 'vant/es/dialog/style'
import 'vant/es/notify/style'
export default function global(app) {
// 注册全局组件
app.component('NewsItem', NewsItem)
app.component('NavBack', NavBack)
// app.component('Lazyload', { Lazyload: true })
app.use(Lazyload)
// 注册全局属性/方法-可以用子组件中用this来访问到。类似于Vue2中,把信息放在Vue.prototype。目的:在视图中可以直接使用这些信息、会挂载到组件的this上(vue2)、可以基于getCurrentInstance获取实例后调用!
app.config.globalProperties.msg = "全局属性"
app.config.globalProperties.$toast = showToast
}
src/components/NewsItem.vue
vant图片懒加载
用一个组件,把vue
与vue-router
,优势在于可以直接使用。但没提示信息,写代码起来会麻烦。同时给不了解的人一些疑惑,不清楚那些方法是从那里导入的。
用自定义hook。优势在于有提示信息,同时更灵活,没有额外学习成本。
src/useAutoImport.js
import * as vue from 'vue'//直接导入vue中全部的api,方便后面使用vue中的东西可以不用再导入。
import { useRouter, useRoute } from 'vue-router'
import { showSuccessToast, showFailToast } from 'vant'
import API from './api'
import dayjs from 'dayjs'
import utils from './assets/utils'
export default function useAutoImport() {
// 二次处理一些事情,直接拿到想要的结果。防止重复进行操作。
const router = useRouter()
const route = useRoute()
// 把想要导出的东西统一return出去。
return {
...vue,//把全部导入的东西原样返回。
router,
route,
dayjs,
API,
showSuccessToast,
showFailToast,
utils
}
}
src/views/Detail.vue
src/useAutoImport.js 定义自定义hooks
import * as vue from 'vue'//直接导入vue中全部的api,方便后面使用vue中的东西可以不用再导入。
import { useRouter, useRoute } from 'vue-router'
import { showSuccessToast, showFailToast } from 'vant'
import API from './api'
import dayjs from 'dayjs'
import utils from './assets/utils'
export default function useAutoImport() {
// 二次处理一些事情,直接拿到想要的结果。防止重复进行操作。
const router = useRouter()
const route = useRoute()
// 把想要导出的东西统一return出去。
return {
...vue,//把全部导入的东西原样返回。
router,
route,
dayjs,
API,
showSuccessToast,
showFailToast,
utils
}
}
src/views/Detail.vue 使用自定义hooks
vue3
中组件缓存用keep-alive
,不过语法和vue2
有区别。新写法可以。
src/App.vue
src/views/Home.vue
旧写法不行。
src/App.vue
src/views/Home.vue
yarn add fastclick
src/main.js
import { FastClick } from 'fastclick'
import { createApp } from 'vue'
// 创建应用
const app = createApp(App)
// 解决click 300ms延迟问题。
FastClick.attach(document.body)
app.mount('#app')
核心
import { FastClick } from 'fastclick'
// 解决click 300ms延迟问题。
FastClick.attach(document.body)
全部代码
src/api/index.js
import http from './http'
// 获取最新的新闻信息
const queryNewsLatest = () => http.get('/news_latest')
// 获取以往的新闻信息
const queryNewsBefore = (time) => {
return http.get('/news_before', {
params: {
time
}
})
}
// 获取新闻的详细信息
const queryNewsInfo = (id) => {
return http.get('/news_info', {
params: {
id
}
})
}
// 获取新闻的点赞信息
const queryStoryExtra = (id) => {
return http.get('/story_extra', {
params: {
id
}
})
}
// 用户登录
const userLogin = (phone, code) => {
return http.post('/login', {
phone,
code
})
}
// 发送验证码
const userSendCode = (phone) => {
return http.post('/phone_code', {
phone
})
}
// 获取登录者信息
const userInfo = () => http.get('/user_info')
// 收藏新闻
const storeAdd = (nwesId) => {
return http.post('/store', {
nwesId
})
}
// 移除收藏
const storeRemove = (id) => {
return http.get('/store_remove', {
params: {
id
}
})
}
// 获取收藏列表
const storeList = (id) => {
return http.get('/store_list')
}
/* 暴露API */
const API = {
queryNewsLatest,
queryNewsBefore,
queryNewsInfo,
queryStoryExtra,
userLogin,
userSendCode,
userInfo,
storeAdd,
storeRemove,
storeList,
}
export default API
src/api/http.js
import axios from 'axios'
import qs from 'qs'
import { showNotify } from 'vant'
import { isPlainObject } from 'lodash'
import utils from '@/assets/utils'
const http = axios.create({
baseURL: '/api',
timeout: 60000,
transformRequest: data => {
if (isPlainObject(data)) return qs.stringify(data)
return data
}
})
const safeList = ['/user_info', '/store', '/store_remove', '/store_list']
http.interceptors.request.use(config => {
if (safeList.includes(config.url)) {
// 请求头携带token。
const token = utils.storage.get('TK')
if (token) {
config.headers['authorzation'] = token
}
}
return config
})
http.interceptors.response.use(response => {
return response.data
}, reason => {
showNotify({
type: 'danger',
message: '网络繁忙,稍后再试!',
duration: 2000
})
return Promise.reject(reason)
})
export default http
核心代码
src/api/index.js
import http from './http'
// 获取最新的新闻信息
const queryNewsLatest = () => http.get('/news_latest')
// 获取以往的新闻信息
const queryNewsBefore = (time) => {
return http.get('/news_before', {
params: {
time
}
})
}
// 获取新闻的详细信息
const queryNewsInfo = (id) => {
return http.get('/news_info', {
params: {
id
}
})
}
// 获取新闻的点赞信息
const queryStoryExtra = (id) => {
return http.get('/story_extra', {
params: {
id
}
})
}
// 用户登录
const userLogin = (phone, code) => {
return http.post('/login', {
phone,
code
})
}
// 发送验证码
const userSendCode = (phone) => {
return http.post('/phone_code', {
phone
})
}
// 获取登录者信息
const userInfo = () => http.get('/user_info')
// 收藏新闻
const storeAdd = (nwesId) => {
return http.post('/store', {
nwesId
})
}
// 移除收藏
const storeRemove = (id) => {
return http.get('/store_remove', {
params: {
id
}
})
}
// 获取收藏列表
const storeList = (id) => {
return http.get('/store_list')
}
/* 暴露API */
const API = {
userLogin,
userSendCode,
userInfo,
storeAdd,
storeRemove,
storeList,
}
export default API
src/api/http.js
import axios from 'axios'
import utils from '@/assets/utils'
const http = axios.create({baseURL: '/api',})
const safeList = ['/user_info', '/store', '/store_remove', '/store_list']
http.interceptors.request.use(config => {
if (safeList.includes(config.url)) {
// 请求头携带token。
const token = utils.storage.get('TK')
if (token) {
config.headers['authorzation'] = token
}
}
return config
})
export default http
通过内置校验进行校验
通过表单实例进行校验
src/views/Login.vue
{{ state.btn.text }}
立即登录/注册
{{ state.btn.text }}
立即登录/注册
把后缀名改为jsx
。
src/components/ButtonAgain.jsx
const ButtonAgain = 哈哈
export default ButtonAgain
src/components/ButtonAgain.jsx
export default function ButtonAgain() {
return 哈哈
}
src/components/ButtonAgain.jsx
import { Button } from "vant"
export default function ButtonAgain() {
return
}
使用时导入并使用就好了。
src/views/Login.vue
import { Button } from 'vant'
export default function ButtonAgain(props, context) {
return
}
import ButtonAgain from './components/ButtonAgain'
export default function global(app) {
app.component('ButtonAgain', ButtonAgain)
}
button按钮loading封装
vue的jsx语法
和react的
不太一样。import { Button } from 'vant'
import { ref, useAttrs, useSlots } from 'vue'
// 把传递的属性,去除特殊的,其余的都赋值给Vant内部的组件
const filter = (attrs) => {
let props = {}
Reflect.ownKeys(attrs).forEach((key) => {
if (key === 'loading' || key === 'onClick') return
props[key] = attrs[key]
})
return props
}
const ButtonAgain = {
inheritAttrs: false,
setup() {
const attrs = useAttrs(),
slots = useSlots()
// 自己控制loading效果
const loading = ref(false)
const handle = async (ev) => {
loading.value = true
try {
await attrs.onClick(ev)
} catch (_) {}
loading.value = false
}
return () => {
let props = filter(useAttrs())//更新时调用。
return (
)
}
}
}
export default ButtonAgain
import { Button } from 'vant'
import { ref, useAttrs, useSlots, computed } from 'vue'
const ButtonAgain = {
inheritAttrs: false,
setup(theProps, context) {
// console.log(`theProps-->`, theProps)
// console.log(`context-->`, context)
const attrs = useAttrs()
const slots = useSlots()
const props = computed(() => {
let theProp = {}
Reflect.ownKeys(attrs).forEach((key) => {
if (key === 'loading' || key === 'onClick') {
return
}
theProp[key] = attrs[key]
})
return theProp
})
const loading = ref(false)
const handle = async (ev) => {
loading.value = true
console.log(`1. loading-->`, loading)
try {
await attrs.onClick(ev)
} catch (error) {
console.log(`error:-->`, error)
}
loading.value = false
console.log(`2. loading.value-->`, loading.value)
}
return () => {
return (
)
}
}
}
export default ButtonAgain