搭建 vite + vue3 + tsx 项目

源代码 2024-10-8 01:22:32 252 0 来自 中国
锁死 npm 版本号
npm config set save-prefix=''1. 创建项目

以下下令二选一
pnpm create vite@2.9.0 mangosteen-fe-1 -- --template vue-tsnpm create vite@2.9.0 mangosteen-fe-1 -- --template vue-ts然后进入项目,分别运行
pnpm run devpnpm run build运行 build 的时间报错
1.png 办理方法:在 tsconfig.json 里添加
{  "compilerOptions": {  +  "skipLibCheck": true,    }}build path

把 HTML、CSS、JS 摆设到 GitHub 或服务器时必须设置 build path
设置规则见文档
在那里配
vite.config.js 里添加 base: '/' 或 '/reponame/' 等
run preview


  • 运行目标
    看看 dist 目次是否能正常运行
  • 约莫等价于
pnpm i http-serverhttp-server -p 4173 dist2.摆设到 Github

1). 将我们的 dist 目次上传,然后把 dist 目次的路径添加到 vite.config.ts 的 base 字段里
export default defineConfig({ +  base: '/bill-fe/dist/',})2). 重新运行
pnpm run build3). push
4). 删除长途的 dist 目次
将我们的 dist 加入到 ignore 里,然后运行
git rm -r --cached dist然后再重新 add commit push
3. template vs tsx

template 写法
<script setup lang="ts">import { ref } from 'vue';const count = ref(0)const onClick = () => {  count.value += 1}</script>tsx 写法
1). 新建一个 .tsx 文件
import { defineComponent, ref } from 'vue';export const App = defineComponent({  setup() {    const refCount = ref(0);    const onClick = () => {      refCount.value += 1;    }  // 这里必要返回一个函数    return () => (      <>        <div>          {refCount.value}        </div>        <div>          <button onClick={onClick}>+1</button>        </div>      </>    )  }})2). 安装 @vitejs/plugin-vue-jsx 插件
pnpm i -D @vitejs/plugin-vue-jsx3). 在 vite.config.ts 里设置 vueJsx
import vueJsx from '@vitejs/plugin-vue-jsx'export default defineConfig({  plugins: [+    vueJsx({      transformOn: true,      mergeProps: true,    })  ]})4. 引入 vue router 4

1). 安装
pnpm i vue-router@42). 利用

  • main.ts
import { createApp } from 'vue'import { createRouter, createWebHashHistory } from 'vue-router';import {App} from './App';import { Bar } from './views/Bar';import { Foo } from './views/Foo';const routes = [  {    path: '/', component: Foo  },  {    path: '/about', component: Bar  }]const router = createRouter({  history: createWebHashHistory(),  routes,})const app = createApp(App)app.use(router)app.mount('#app')

  • App.tsx
import { defineComponent } from 'vue';import { RouterView } from 'vue-router';export const App = defineComponent({  setup() {    return () => (      <RouterView />    )  }})5. 利用 css module 和全局 css

利用 css module

1). 在当前目次下创建一个.module.scss 文件
2). 引入这个 css 文件通过变量名的情势
3). 通过 s.样式名来利用

  • Welcome.module.scss
.wrapper {  color: red;}

  • Welcome.tsx
import { defineComponent } from 'vue';import s from './Welcome.module.scss';export const Welcome = defineComponent({  setup: (props, context) => {    return () => (      <div class={s.wrapper}>        aaa      </div>    )  }});由于我们用的是 sass 以是必要利用 pnpm i sass
利用全局 css

1). 新建一个.css 文件
2). 直接通过 import './***.css' 引入
6. 利用 slot 插槽

import { defineComponent } from 'vue';import s from './First.module.scss';export const First = defineComponent({  setup: (props, {slots}) => {    return () => (      <div class={s.wrapper}>        <div class={s.card}>          {slots.icon?.()}          {slots.title?.()}        </div>        <div class={s.actions}>          {slots.buttons?.()}        </div>      </div>    )  }})

  • demo
import { WelcomeLayout } from './WelcomeLayout';export const First = defineComponent({  setup: (props, context) => {    const slots = {      icon: () => <span>icon</span>,      title: () => 'hi',      buttons: () => <><button>+1</button></>    }    return () => (     <WelcomeLayout v-slots={slots} />    )  }})大概export const First = defineComponent({  setup: (props, context) => {    return () => (     <WelcomeLayout>      {{        icon: () => <span>icon</span>,        title: () => 'hi',        buttons: () => <><button>+1</button></>      }}      </WelcomeLayout>    )  }})7. 利用多个 RouterView

router.tsx
{    path: '/welcome',    component: Welcome,    children: [      { path: '', redirect: '/welcome/1', },      { path: '1', components: { main: First, footer: FirstActions }, },      { path: '2', components: { main: Second, footer: SecondActions }, },      { path: '3', components: { main: Third, footer: ThirdActions }, },      { path: '4', components: { main: Forth, footer: ForthActions }, },    ]  }

  • demo
import { RouterView } from 'vue-router';export const Welcome = defineComponent({  setup: (props, context) => {    return () => <div class={s.wrapper}>      <header>        <img src={logo} />        <h1>山竹记账</h1>      </header>      <main class={s.main}><RouterView name="main" /></main>      <footer>        <RouterView name="footer" />      </footer>    </div>  }})路由动画
<main class={s.main}>        <RouterView name="main">          {({Component: Content, route: R}: { Component: VNode, route: RouteLocationNormalizedLoaded}) => (            <Transition              enterFromClass={s.slide_fade_enter_from}              enterActiveClass={s.slide_fade_enter_active}              leaveToClass={s.slide_fade_leave_to}              leaveActiveClass={s.slide_fade_leave_active}            >              {Content}            </Transition>          )}        </RouterView>      </main>.slide_fade_enter_active,.slide_fade_leave_active {  position: absolute;  left: 0;  top: 0;  width: 100%;  height: 100%;  transition: all 0.5s ease-out;}.slide_fade_enter_from {  transform: translateX(100vw);}.slide_fade_leave_to {  transform: translateX(-100vw);}8. 写一个svg vite 插件用来预加载所有的svg

标题:我们页面的svg在路由切换的时间有大概还没加载完成,会出现图片加载慢的标题
办理:
1). 安装 svgo 和 svgstore
pnpm i svgo svgstore2). 创建 vite_plugins/svgstore.js
/* eslint-disable */import path from 'path'import fs from 'fs'import store from 'svgstore' // 用于制作 SVG Spritesimport { optimize } from 'svgo' // 用于优化 SVG 文件export const svgstore = (options = {}) => {  const inputFolder = options.inputFolder || 'src/assets/icons';  return {    name: 'svgstore',    // 分析 假如文件是 @svgstore 直接加载 svg_bundle.js    // 引入的时间直接利用 import '@svgstore'    resolveId(id) {      if (id === '@svgstore') {        return 'svg_bundle.js'      }    },    load(id) {      if (id === 'svg_bundle.js') {        // 创建一个大的 svg        const sprites = store(options);        const iconsDir = path.resolve(inputFolder);        // 遍历所有的svg,然后把每一个都添加到这个大的里        for (const file of fs.readdirSync(iconsDir)) {          const filepath = path.join(iconsDir, file);          const svgid = path.parse(file).name          let code = fs.readFileSync(filepath, { encoding: 'utf-8' });          sprites.add(svgid, code)        }        // 优化大的 svg        const { data: code } = optimize(sprites.toString({ inline: options.inline }), {          plugins: [            'cleanupAttrs', 'removeDoctype', 'removeComments', 'removeTitle', 'removeDesc',             'removeEmptyAttrs',            { name: "removeAttrs", params: { attrs: "(data-name|data-xxx)" } }          ]        })        // 把这个大的 svg 变成js文件        return `const div = document.createElement('div')div.innerHTML = \`${code}\`const svg = div.getElementsByTagName('svg')[0]if (svg) {  svg.style.position = 'absolute'  svg.style.width = 0  svg.style.height = 0  svg.style.overflow = 'hidden'  svg.setAttribute("aria-hidden", "true")}// listen dom ready eventdocument.addEventListener('DOMContentLoaded', () => {  if (document.body.firstChild) {    document.body.insertBefore(div, document.body.firstChild)  } else {    document.body.appendChild(div)  }})`      }    }  }}3). 在 vite.config.ts 里注册这个设置
import { svgstore } from './src/vite_plugins/svgstore';export default defineConfig({  plugins: [   + svgstore(),  ]})4). 在入口文件中引入我们的svgstore

  • main.ts
import '@svgstore';5). 将我们的 <img> 标签换成 svg
<svg>    <use xlinkHref='#chart'></use></svg>9. hooks


  • useSwipe
import { computed, onMounted, onUnmounted, ref, Ref } from "vue"type Point = {  x: number;  y: number;}export const useSwipe = (element: Ref<HTMLElement | null>) => {  const start = ref<oint | null>(null)  const end = ref<oint | null>(null)  const swiping = ref(false)  const distance = computed(() => {    if (!start.value || !end.value) { return null }    return {      x: end.value.x - start.value.x,      y: end.value.y - start.value.y,    }  })  const direction = computed(() => {    if (!distance.value) { return '' }    const { x, y } = distance.value    if (Math.abs(x) > Math.abs(y)) {      return x > 0 ? 'right' : 'left'    } else {      return y > 0 ? 'down' : 'up'    }  })  const onStart = (event: TouchEvent) => {    swiping.value = true    end.value = start.value = { x: event.touches[0].screenX, y: event.touches[0].screenY }  }  const onMove = (event: TouchEvent) => {    if (!start.value) { return }    end.value = { x: event.touches[0].screenX, y: event.touches[0].screenY, }  }  const onEnd = (event: TouchEvent) => {    swiping.value = false  }  onMounted(() => {    if (element.value) {      element.value.addEventListener('touchstart', onStart)      element.value.addEventListener('touchmove', onMove)      element.value.addEventListener('touchend', onEnd)    }  })  onUnmounted(() => {    if (element.value) {      element.value.removeEventListener('touchstart', onStart)      element.value.removeEventListener('touchmove', onMove)      element.value.removeEventListener('touchend', onEnd)    }  })  return {    swiping,    direction,    distance  }}利用
export const Welcome = defineComponent({  setup: (props, context) => {    const main = ref<HTMLElement | null>(null)    const { direction, swiping } = useSwipe(main)    return () => (      <main ref={main/>    )}10. 自界说组件范例声明


  • 子组件
// 方法1interface Props {  onClick: (event: MouseEvent) => void;  name: 'add' | 'chart';}export const Button = defineComponent<rops>({  setUp: (props, context) => {   // 利用<rops> 这种方式只有内置的属性才气访问到 onClick 是内置的以是能访问到    console.log(props.onClick)    // name 内部没有界说以是访问不到    console.log(props.name)  }})// 方法2(获取我们本身界说的 props)export const Button = defineComponent({  props: {    name: {      // String 是js PropType内里是 ts      type: String as PropType<'add' | 'chart'>    }  }  setUp: (props, context) => {    console.log(props.name)  }})

  • 父组件
const onClick = () => {}<Button onClick={onClick} name={'lifa'}>按钮</Button>11. 打包静态资源

假如我们必要引入图片资源有两种方式
1). 把图片资源放到 public 目次里,直接通过 public 目次下的路径引入

  • public/images/logo.png
<img src="/images/logo.png" />如许我们打包后 dist 目次下就会多一个 images 文件内里有我们的 logo.png
2). 我们本身创建的目次,好比我在 src/assets/icons/logo.png
那么我们可以通过 import 语法
import logo from "@/assets/icons/logo.png";<img src={logo}如许打包后就会天生一个 asset/logo.chunk值.png
12. proxy

利用 proxy 就是 将你当地的 localhost:3000/api 署理到对应的后端域名,
以是肯定要保证我们是通过 localhost 来调这个接口的,假如利用axios的话,baseUrl 要写成 /
server: {      // Listening on all local IPs      cors: true,      proxy: {        "/api": {          target: "http://f2e-sit.ccc.com",          changeOrigin: true,          rewrite: (path) => path.replace(/^\/api/, ""),        },      },    },

如许我们调 localhost:3000/api 就会署理到 http://f2e-sit.ccc.com
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-24 19:23, Processed in 0.164708 second(s), 36 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表