20230725----重返学习-vue3项目实战-知乎日报第2天

day-120-one-hundred-and-twenty-20230725-vue3项目实战-知乎日报第2天

vue3项目实战-知乎日报第2天

新闻列表

  1. 每一天的新闻都是一个可遍历的区块,一天中的新闻有多个新闻,每一个新闻都是一个组件。
  2. 用到的日期要转格式。
  3. 组件要注册为全局组件。可通过插件把components目录中的组件变成全局组件。也可以自己注册全局组件-更灵活,也可添加函数式调用的组件。
  4. 骨架屏用v-ifv-else来区分,非一个独立的根节点的地方,用template标签包起来。
  • src/views/Home.vue





组件封装

  1. 决定组件怎样用,是函数式组件还是模板式组件。
  2. 组件的复杂与灵活度,决定是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图片懒加载

触底加载

方法自动导入

  • 有两种方案。
  1. 用一个组件,把vuevue-router,优势在于可以直接使用。但没提示信息,写代码起来会麻烦。同时给不了解的人一些疑惑,不清楚那些方法是从那里导入的。

  2. 用自定义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

      
      

自定义hook

  1. 一般有业务的组件之类的方法和状态中,可以使用自定义hooks。
  2. 一般通用逻辑中,也用的自定义hooks。
  • 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

    
    

组件缓存

  1. vue3中组件缓存用keep-alive,不过语法和vue2有区别。
  • 新写法可以。

    • src/App.vue

      
      
    • src/views/Home.vue

      
      
  • 旧写法不行。

    • src/App.vue

      
      
    • src/views/Home.vue

      
      

详情页

并行请求


动态加载样式

  1. 创建一个link标签。

渲染头图

  • 等待组件更新完毕之后,再进行更新外部传入的html结构。

点击事件延时问题

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)
    

登录页

特定请求添加token

  • 全部代码

    • 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
      

规则校验

  1. 表单校验总结:
    • 有一个form表单,它对应一些实例。
    • form内部有form-item。
    • 收集各个表单的name。
    • 用v-model绑定到表单数据中。
    • 有一个name。
    • 规则校验
      • 一般写到各个表单上。或者全部放在form上。
      • 内置规则
      • 自定义规则:正则或函数。
    • 触发检验:
      • 自动触发-用表单中的submit类型按钮点击后,执行。
      • 手动触发-通过表单实例。
  • 通过内置校验进行校验

  • 通过表单实例进行校验

  • src/views/Login.vue




发送验证码

  1. 对手机号单独做校验-表单的手动校验。
  2. 向服务器发送请求。
  3. 禁止用户点击,同时开启定时器。
  • src/views/Login.vue






提交表单信息

  1. 对表单进行校验。
  2. 发送请求。
  3. 登录成功:存储token、进行提示。
  4. 获取登录者信息、进行页面的跳转。

jsx组件

  1. 把后缀名改为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 
      }
      
  2. 使用时导入并使用就好了。

    • src/views/Login.vue

      
      
      
      
      

注册jsx全局组件

  • src/components/ButtonAgain.jsx
import { Button } from 'vant'

export default function ButtonAgain(props, context) {
  return 
}
  • src/global.js
import ButtonAgain from './components/ButtonAgain'
export default function global(app) {
    app.component('ButtonAgain', ButtonAgain)
}
  • src/views/Login.vue




button按钮loading封装

  1. vue的jsx语法react的不太一样。
  • src/components/ButtonAgain.jsx 不用计算属性。
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
  • src/components/ButtonAgain.jsx 使用计算属性之后。
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

进阶参考

你可能感兴趣的:(重返学习,vue,学习,vue.js,前端)