跳到主要内容

4 篇博文 含有标签「vue」

查看所有标签

· 阅读需 3 分钟
JaguarJack

为什么会有这么个需求呢

最近在开发CMS,所以想直接分发 cms 的打包后的代码。但是这里面就有个问题,如果我将打包后的代码直接让用户使用,那么不得不面临一个问题,就是打包后的接口地址也会直接打包给了用户。所以这里就需要用户自己去设置接口地址。但是前端是打包后的代码,没有办法去修改,所以就需要这么一个动态修改配置的功能

挂载

如何进行挂载呢?首先需要在入口文件index.html引入

<div id="app"></div>
<script type="module" src="../resources/admin/app.ts"></script>

// 就在这里引入
<script src="./static/config.js"></script>

千万不要纠结这个路径,因为这是打包后的路径,所以开发的时候也不需要管它。

第二步,如果没有public目录,在项目的任意目录创建 public 目录,在public目录下创建 static/config.js, 内容如下

window.admin_config = {
title: '',
BASE_URL: '',
}

然后找到 axios 请求,CatchAdmin进行了封装,所以使用的是 support\http.ts。 在这之前 CatchAdmin 还封装了两个助手方法

// 从 window 对象获取配置
// 因为动态配置挂载在 window 的 admin.config 属性
export function _window(key: string) {
if (window.hasOwnProperty('admin_config')) {
//@ts-ignore
return window.admin_config[key]
}

return null
}

// 获取 base_url
export function getBaseUrl() {
// 如果动态配置存在并且 BASE_URL 有值,则从动态配置获取
// 否则从 env 配置获取
return _window('BASE_URL') ? _window('BASE_URL') : env('VITE_BASE_URL')
}

再到 http.ts 获取 baseURL

  protected getConfig(): AxiosRequestConfig {
// set base url
this.config.baseURL = getBaseUrl()
}

这样就可以了。那会有人有疑问?./static/config.js 怎么引入的呢? 在 build 打包后,public 会被打包到 index.html 平级目录,所以可以直接引入。对了,忘了一个很重要的,就是配置 public 目录,找到 vite.config.js 文件

export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd(), '')
return {
publicDir: 'public 目录相对路径',
}
}

· 阅读需 6 分钟
JaguarJack

为什么需要组合开发

夏至冬来的一次久违的分享!从之前 catchadmin 开发情况来看,使用该项目的多是后端人员,极大可能还是前后端都有参与,所以将两者组合到一起会很方便开发者。当然如果需要单独开发也没有问题,将该篇 blog 的操作逆向看也行。而且 LaravelVue 集合也是相当好,生态也很丰富,反观 thinkphp, 简直一塌糊涂,每每小版本升级都导致很多问题。这种事情在 v3 下会很少发生了。

迁移

首先 v3 也是有前端的,但是 v3 前端的仓库将会是单独的纯前端仓库,不会再跟后台耦合。所以在开发前第一步就是要将前端迁移到 Laravel。当然这篇 blog 只是记录过程,实际上项目上已经完全迁移好了

项目文件

前端项目一般都是根目录存有依赖,config 配置等文件,src 则是开发目录,首先是将根目录的依赖 config 复制到 Laravel 项目根目录下

  • package.json
  • .env
  • .env.production
  • postcss.config.js
  • tailwind.config.js
  • tsconfig.json
  • tsconfig.node.json
  • vite.config.js

等等,上面列出主要文件

开发目录迁移

Laravel 一般规定前端开发的文件都存储在 resources 目录,所以先在该目录下创建 admin 目录, 然后将前端的 src 目前复制到 Laravel 的 resources/admin 目录。迁移进去之后,先不要着急运行 yarn dev,首先安装 Laravel Vite 开发插件

yarn add -D laravel-vite-plugin

在 resources/views 目录下建立 admin.blade.php 文件,将前端的 index.html 内容复制到该文件中,目前 v3 内容如下

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Laravel</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
width: 100%;
height: 100%;
}
</style>
<!-- 这里引入前端的入口文件 -->
@vite('resources/admin/app.ts')
</head>
<body id="app">
</body>
</html>

配置 vite.config.js

这个配置是最重要的一环,首先引入

import laravel from 'laravel-vite-plugin'

return {
plugins: [
laravel({
input: ['resources/admin/app.ts'],
// 监控目录刷新
refresh: ['resources/admin'],
}),
// 前端都是使用 @ 作为项目根目录,但是这里不能这么使用
// 即使使用 @ => resources/admin 也不会生效
// 因为 laravel-vite-plugin 在插件写死 @ => resources/js
// 虽然可以覆盖,但由于在写下 blog 的时候,vite 3.2 的配置和 3.0 的配置有所区别,导致无法覆盖
// 所以这里使用 /admin 前缀表示
// @ 留给自主开发应用
alias({
entries: [
{
find: '/admin',
replacement: resolve(rootPath, 'resources/admin'),
},
],
}),
// 自动生成全局组件
// 不需要在 import 组件
Components({
dirs: ['resources/admin/components/', 'resources/admin/layout/'],

extensions: ['vue'],

deep: true,

dts: true,

include: [/\.vue$/, /\.vue\?vue/],

exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/],
// resolvers: [ ElementPlusResolver({ importStyle: 'sass'}) ]
}),
]
}

目前就需要这么多的配置,后期改变继续添加

配置 tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
mode: 'jit',
darkMode: 'class',
// 主要
content: ['./resources/admin/**/*.{vue,js,ts,jsx.tsx}'],
theme: {
extend: {
transitionProperty: {
width: 'width',
spacing: 'margin, padding'
},
colors: {
'regal-dark': '#283046'
}
}
},
plugins: []
}

配置 ts

{
"compilerOptions": {
"baseUrl": "./",
"target": "esnext",
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"allowJs": true,
"lib": [
"esnext",
"dom"
],
"skipLibCheck": true,
// "types": ["element-plus/golbal"],
"paths": {
"@/*": [
"resources/js/*"
],
// 主要
"/admin/*": [
"resources/admin/*"
]
}
},
"include": [
// 主要配置
"resources/admin/js/**/*.ts",
"resources/admin/js/**/*.d.ts",
"resources/admin/js/**/*.tsx",
"resources/admin/js/**/*.vue",
"resources/admin/js/**/*.js",
"resources/admin/js/env.d.ts"
],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}

env

因为 Laravel 目录也是有 .env 文件,所以将 vue 的项目 .envLaravel 的 .env 合并,主要就是添加一个配置项

VITE_BASE_URL=http://127.0.0.1:9091/api/

代码格式化

PHPStorm 安装 prettier 插件,然后在 preferences 中选择 Languages & Frameworks, 选中 JavascriptPrettier, 会看到 prettier package, 因为项目已经安装 prettier 插件,所以选择 node_modules/prettier,再勾选 On Save 选项。

另外项目的根目录下有一个 .prettierrc 文件,内容如下

{
// 句末加分号
"semi": false,
"printWidth": 200,
"tabWidth": 2,
"useTabs": false,
// 用单引号
"singleQuote": true,
// 箭头函数参数括号 (x) => {} always
// x => {} avoid
"arrowParens": "avoid",
"trailingComma": "all",
// 对象&数组是否追加空格
"bracketSpacing": true
}

最后

找到 routes/web.php 文件,添加路由

Route::get('/', function () {
return view('admin');
});

使用

 yarn dev

启动前端项目

使用

php artisan serve

启动后端项目,通过后端的链接访问项目,大功完成✅

· 阅读需 4 分钟
JaguarJack

Piniavue3 新推的状态管理,当然你也可以使用 vuex, 但是不是很建议,因为这个 plugin 就相当于 vuex5 了。

为什么使用它

引用官方文档的内容 PiniaVue 的存储库,它允许您跨组件/页面共享状态。 如果您熟悉 Composition API,您可能会认为您已经可以通过一个简单的 export const state = reactive({}). 这对于单页应用程序来说是正确的,但如果它是服务器端呈现的,会使您的应用程序暴露于安全漏洞。 但即使在小型单页应用程序中,您也可以从使用 Pinia 中获得很多好处:

  • dev-tools 支持
    • 跟踪动作、突变的时间线
    • Store 出现在使用它们的组件中
    • time travel 和 更容易的调试
  • 热模块更换
    • 在不重新加载页面的情况下修改您的 Store
    • 在开发时保持任何现有状态
  • 插件:使用插件扩展 Pinia 功能
  • JS 用户提供适当的 TypeScript 支持或 autocompletion
  • 服务器端渲染支持

安装

yarn add pinia
# 或者使用 npm
npm install pinia

使用

找到 utils/catchadmin.ts, 全局引入

import { createPinia } from 'pinia'

protected usePinia () : CatchAdmin {
this.app.use(createPinia())

return this
}

创建 store 文件夹

mkdir src/store

// then

touch index.ts

// 保留主入口

创建模块

cd src/store && mkdir modules

创建 app Store

cd modules && mkdir app

// then

touch index.ts

内容如下

import { defineStore } from 'pinia'

const initSize = 'small'

const initLocale = 'zh'

/**
* app
*/
type app = {
size: 'small' | 'medium' | 'large',

isExpand: boolean,

locale: 'zh' | 'en',

isMobile: boolean
}

export const useAppStore = defineStore('app', {
state: () : app => ({
size: initSize,
isExpand: true,
locale: initLocale,
isMobile: false
}),

actions: {
changeSize (size: 'small' | 'medium' | 'large') {
this.size = size
},

changeLocale (locale: 'zh' | 'en') {
this.locale = locale
},

changeExpaned () {
this.isExpand = !this.isExpand
}
}
})

这里还是用的 optional api,本人体验了下 composition api 写法,似乎很麻烦,然后参考了下相关的项目的写法,都是使用的 optional api 写法。

为什么要使用

在项目中有一些状态需要在组件之间流转,例如,目前在写这个 blog 文章的时候,项目遇到的菜单「展开关闭」这个状态值,这个值需要在 header 组件,Slide 组件及其子组件之间不停的流转,就很麻烦,不仅需要 props 还需要 emit 这样的操作无疑增加了代码的复杂度以及维护的难度,使用状态管理之后,因为状态管理是响应式的,所以可以通过一个接口,就可以对状态进行操作,很方便。

找到 layout/componetns/header/index.vue

import { useAppStore } from '@/stores/modules/app'

const store = useAppStore()

const { changeExpaned } = store

就可以直接在组件中使用了 changeExpaned 方法,对应的状态也可以改变

· 阅读需 2 分钟
JaguarJack

原因

为什么要使用第三方的图标?因为 ElementPlusicon 实在是太少了,不够用。hreIcons 感觉还可以,并且可以很好的和 tailwindcss 集合,图标数量也有 230+ ,感觉能满足后台需求。所以整个项目为了 icon 统一,就去除了 ElementPlus Icon 了,统一使用 heroIcons

安装

yarn add @heroicons/vue  

使用

为了统一使用,并且为了之后的动态 icon 做铺垫,所以这里需要二次封装下。创建 src/icon/index.vue 组件。

<template>
<component :is="icon" class="ct-w-5 ct-h-5"/>
</template>

<script>
import { defineComponent } from 'vue'
import * as heroIcons from '@heroicons/vue/outline'
export default defineComponent({
name: 'icon',
props: {
name: {
type: String,
required: true
}
},
setup (props, ctx) {
let name = ''

props.name.split('-').forEach(v => {
name += v[0].toUpperCase() + v.substr(1)
})

const icon = heroIcons[name + 'Icon']

return {
icon
}
}
})
</script>

创建完成之后,为了可以全局使用,需要注册在 vue 中注册下,还是找到后台的入口 utils/catchadmin.ts

import icon from '@/components/icon/index.vue'


// 找到 registerComponents 方法,注册
protected registerComponent () : CatchAdmin {
this.app.component('icon', icon)

return this
}

注册完成之后,就可以全局使用了,例如这样

<Icon name="logout"/>

当然这个使用是有一个小小弊端,修改之后无法立即刷新图标,但是这个应该不是什么大问题,因为图标是静态的。 当然如果你不喜欢这样,也可以直接在页面中引入 heroIcons

<template>
<div>
<BeakerIcon class="h-5 w-5 text-blue-500"/>
<p>...</p>
</div>
</template>

<script>
import { BeakerIcon } from '@heroicons/vue/solid'

export default {
components: { BeakerIcon }
}
</script>