邯郸网站建设提供商驻马店百度seo
一、HTML、JS、CSS
1、环境准备
(1、)安装NVM
(2、)检查NPM
(3、)搭建简单服务
2、Fetch-API
二、框架
1、VUE2
(1、)环境准备
1、)安装脚手架
npm install -g @vue/cli
-
-g 参数表示全局安装,这样在任意目录都可以使用 vue 脚本创建项目
2、)创建项目
vue ui
使用图形向导来创建 vue 项目,如下图,输入项目名
选择手动配置项目
添加 vue router 和 vuex
选择版本,创建项目
3、)安装 devtools
-
devtools 插件网址:Installation | Vue Devtools
4、)运行项目
进入项目目录,执行
npm run serve
5、)修改端口
前端服务器默认占用了 8080 端口,需要修改一下
-
文档地址:DevServer | webpack
-
打开 vue.config.js 添加
const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({// ...devServer: {port: 7070}})
6、)添加代理
为了避免前后端服务器联调时, fetch、xhr 请求产生跨域问题,需要配置代理
-
文档地址同上
-
打开 vue.config.js 添加
const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({// ...devServer: {port: 7070,proxy: {'/api': {target: 'http://localhost:8080',changeOrigin: true}}}})
2、VUE3
(1、)环境准备
1、)创建项目
采用 vite 作为前端项目的打包,构建工具
npm init vite@latest
按提示操作
cd 项目目录 npm install npm run dev
2、)编码 IDE
推荐采用微软的 VSCode 作为开发工具,到它的官网 Visual Studio Code - Code Editing. Redefined 下载安装即可
要对 *.vue 做语法支持,还要安装一个 Volar 插件
3、)安装 devtools
-
devtools 插件网址:Installation | Vue Devtools
4、)修改端口
打开项目根目录下 vite.config.ts
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' // https://vitejs.dev/config/ export default defineConfig({plugins: [vue()],server: {port: 7070} })
-
文档地址:配置 Vite {#configuring-vite} | Vite中文网 (vitejs.cn)
5、)配置代理
为了避免前后端服务器联调时, fetch、xhr 请求产生跨域问题,需要配置代理,同样是修改项目根目录下 vite.config.ts
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' // https://vitejs.dev/config/ export default defineConfig({plugins: [vue()],server: {port: 7070,proxy: {'/api': {target: 'http://localhost:8080',changeOrigin: true}}} })
-
文档地址:配置 Vite {#configuring-vite} | Vite中文网 (vitejs.cn)
3、REACT
(1、) 环境准备
1、)创建项目
首先,通过 react 脚手架创建项目
npx create-react-app client --template typescript
-
client 是项目名
-
目前 react 版本是 18.x
2、)运行项目
cd client npm start
-
会自动打开浏览器,默认监听 3000 端口
3、)修改端口
在项目根目录下新建文件 .env.development,它可以定义开发环境下的环境变量
PORT=7070
重启项目,端口就变成了 7070
-
参考文档:Advanced Configuration | Create React App (create-react-app.dev)
4、)浏览器插件
插件地址 New React Developer Tools – React Blog (reactjs.org)
VSCode
推荐安装 Prettier 代码格式化插件
三、组件库
1、Element-UI
2、Ant Design
(1、)react 组件库
-
官网地址:Ant Design - The world's second most popular React UI framework
-
镜像地址1:Ant Design - The world's second most popular React UI framework
-
镜像地址2:Ant Design - The world's second most popular React UI framework
(2、)安装
npm install antd
-
目前版本是 4.x
引入样式,在 css 文件中加入
@import '~antd/dist/antd.css';
引入 antd 组件
import { Button } from "antd"; export default function A1() {return <Button type='primary'>按钮</Button> }
(3、)国际化
试试其它组件
import { Button, Modal } from "antd"; export default function A1() {return <Modal open={true} title='对话框'>内容</Modal> }
发现确定和取消按钮是英文的,这是因为 antd 支持多种语言,而默认语言是英文
要想改为中文,建议修改最外层的组件 index.tsx
// ... import { ConfigProvider } from 'antd' import zhCN from 'antd/es/locale/zh_CN' root.render(<ConfigProvider locale={zhCN}><A1></A1></ConfigProvider> )
四、扩展
1、跨域问题
(1、)后端解决
@CrossOrigin(“允许路径”)
(2、)前端解决(代理)
2、TypeScript
(1、)环境准备
1、)安装 typescript 编译器
npm install -g typescript
2、)编写 ts 代码
function hello(msg: string) {console.log(msg) } hello('hello,world')
3、)执行 tsc 编译命令
tsc xxx.ts
4、)编译生成 js 代码,编译后进行了类型擦除
function hello(msg) {console.log(msg); } hello('hello,world');
5、)再来一个例子,用 interface 定义用户类型
interface User {name: string,age: number } function test(u: User): void {console.log(u.name)console.log(u.age) } test({ name: 'zhangs', age: 18 })
6、)编译后
function test(u) {console.log(u.name);console.log(u.age); } test({ name: 'zhangs', age: 18 });
可见,typescript 属于编译时实施类型检查(静态类型)的技术
五、数据共享工具
1、VUEX
2、PINIA
3、MOBX
(1、)文档
-
MobX 中文文档
-
MobX 官方文档
(2、)安装
npm install mobx mobx-react-lite
-
mobx 目前版本是 6.x
-
mobx-react-lite 目前版本是 3.x
(3、)名词
-
Actions 用来修改状态数据的方法
-
Observable state 状态数据,可观察
-
Derived values 派生值,也叫 Computed values 计算值,会根据状态数据的改变而改变,具有缓存功能
-
Reactions 状态数据发生变化后要执行的操作,如 react 函数组件被重新渲染
(4、)使用
首先,定义一个在函数之外存储状态数据的 store,它与 useState 不同:
-
useState 里的状态数据是存储在每个组件节点上,不同组件之间没法共享
-
而 MobX 的 store 就是一个普通 js 对象,只要保证多个组件都访问此对象即可
import axios from 'axios' import { makeAutoObservable } from 'mobx' import { R, Student } from '../model/Student' class StudentStore {student: Student = { name: '' } constructor() {makeAutoObservable(this)} async fetch(id: number) {const resp = await axios.get<R<Student>>(`http://localhost:8080/api/students/${id}`)runInAction(() => {this.student = resp.data.data})}get print() {const first = this.student.name.charAt(0)if (this.student.sex === '男') {return first.concat('大侠')} else if (this.student.sex === '女') {return first.concat('女侠')} else {return ''}} } export default new StudentStore()
其中 makeAutoObservable 会
-
将对象的属性 student 变成 Observable state,即状态数据
-
将对象的方法 fetch 变成 Action,即修改数据的方法
-
将 get 方法变成 Computed values
在异步操作里为状态属性赋值,需要放在 runInAction 里,否则会有警告错误
使用 store,所有使用 store 的组件,为了感知状态数据的变化,需要用 observer 包装,对应着图中 reactions
import Search from 'antd/lib/input/Search' import { observer } from 'mobx-react-lite' import studentStore from '../store/StudentStore' import A71 from './A71' import Test2 from './Test2' const A7 = () => {return (<div><Searchplaceholder='input search text'onSearch={(v) => studentStore.fetch(Number(v))}style={{ width: 100 }}/><h3>组件0 {studentStore.student.name}</h3><A71></A71><A72></A72></div>) } export default observer(A7)
其它组件
import { observer } from 'mobx-react-lite' import studentStore from '../store/StudentStore' const A71 = () =>{return <h3 style={{color:'red'}}>组件1 {studentStore.student.name}</h3> } export default observer(A71)import { observer } from 'mobx-react-lite' import studentStore from '../store/StudentStore' const A72 = () =>{return <h3 style={{color:'red'}}>组件1 {studentStore.student.name}</h3> } export default observer(A72)
(5、)注解方式
import { R, Student } from "../model/Student"; import { action, computed, makeAutoObservable, makeObservable, observable, runInAction } from 'mobx' import axios from "axios"; class StudentStore {// 属性 - 对应状态数据 observable state@observable student: Student = { id: 0, name: '' }// 方法 - 对应 action 方法@action setName(name: string) {this.student.name = name}@action async fetch(id: number) {const resp = await axios.get<R<Student>>(`http://localhost:8080/api/students/${id}`)runInAction(() => {this.student = resp.data.data})}// get 方法 - 对应 derived value@computed get displayName() {const first = this.student.name.charAt(0)if (this.student.sex === '男') {return first + '大侠'} else if (this.student.sex === '女') {return first + '女侠'} else {return ''}}// 构造器constructor() {makeObservable(this)} } export default new StudentStore()
需要在 tsconifg.json 中加入配置
{"compilerOptions": {// ..."experimentalDecorators": true} }
六、Router
1、Vue Router
2、React Router
(1、)安装
npm install react-router-dom
-
目前版本是 6.x
(2、)使用
新建文件 src/router/router.tsx
import { lazy } from 'react' import { Navigate, RouteObject, useRoutes } from 'react-router-dom' export function load(name: string) {const Page = lazy(() => import(`../pages/${name}`))return <Page></Page> } const staticRoutes: RouteObject[] = [{ path: '/login', element: load('A8Login') },{path: '/',element: load('A8Main'),children: [{ path: 'student', element: load('A8MainStudent') },{ path: 'teacher', element: load('A8MainTeacher') },{ path: 'user', element: load('A8MainUser') }],},{ path: '/404', element: load('A8Notfound') },{ path: '/*', element: <Navigate to={'/404'}></Navigate> }, ] export default function Router() {return useRoutes(staticRoutes) }
index.tsx 修改为
import ReactDOM from 'react-dom/client'; import './index.css'; import { ConfigProvider } from 'antd'; import zhCN from 'antd/es/locale/zh_CN' import { BrowserRouter } from 'react-router-dom'; import Router from './router/router'; const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement ); root.render(<ConfigProvider locale={zhCN}><BrowserRouter><Router></Router></BrowserRouter></ConfigProvider> )
A8Main 的代码
import { Layout } from "antd"; import { Link, Outlet } from "react-router-dom"; export default function A8Main () { return <Layout><Layout.Header>头部导航</Layout.Header><Layout><Layout.Sider>侧边导航<Link to='/student'>学生管理</Link><Link to='/teacher'>教师管理</Link><Link to='/user'>用户管理</Link></Layout.Sider><Layout.Content><Outlet></Outlet></Layout.Content></Layout></Layout> }
-
Navigate 的作用是重定向
-
load 方法的作用是懒加载组件,更重要的是根据字符串找到真正的组件,这是动态路由所需要的
-
children 来进行嵌套路由映射,嵌套路由在跳转后,并不是替换整个页面,而是用新页面替换父页面的 Outlet 部分
(3、)动态路由
路由分成两部分:
-
静态路由,固定的部分,如主页、404、login 这几个页面
-
动态路由,变化的部分,经常是主页内的嵌套路由,比如 Student、Teacher 这些
动态路由应该是根据用户登录后,根据角色的不同,从后端服务获取,因为这些数据是变化的,所以用 mobx 来管理
import axios from 'axios' import { makeAutoObservable, runInAction } from 'mobx' import { Navigate, RouteObject } from 'react-router-dom' import { MenuAndRoute, R, Route } from '../model/Student' import { load } from '../router/MyRouter' class RoutesStore {dynamicRoutes: Route[] async fetch(username: string) {const resp = await axios.get<R<MenuAndRoute>>(`http://localhost:8080/api/menu/${username}`)runInAction(() => {this.dynamicRoutes = resp.data.data.routeListlocalStorage.setItem('dynamicRoutes', JSON.stringify(this.dynamicRoutes))})} constructor() {makeAutoObservable(this)const r = localStorage.getItem('dynamicRoutes')this.dynamicRoutes = r ? JSON.parse(r) : []} reset() {this.dynamicRoutes = []localStorage.removeItem('dynamicRoutes')} get routes() {const staticRoutes: RouteObject[] = [{ path: '/login', element: load('A8Login') },{ path: '/', element: load('A8Main') },{ path: '/404', element: load('A8Notfound') },{ path: '/*', element: <Navigate to={'/404'}></Navigate> },]const main = staticRoutes[1] main.children = this.dynamicRoutes.map((r) => {console.log(r.path, r.element)return {path: r.path,element: load(r.element),}})return staticRoutes} } export default new RoutesStore()
-
其中用 localStorage 进行了数据的持久化,避免刷新后丢失数据
MyRouter 文件修改为
import { observer } from 'mobx-react-lite' import { lazy } from 'react' import { Navigate, RouteObject, useRoutes } from 'react-router-dom' import RoutesStore from '../store/RoutesStore' // 把字符串组件 => 组件标签 export function load(name: string) {// A8Loginconst Page = lazy(() => import(`../pages/${name}`))return <Page></Page> } // 路由对象 function MyRouter() { const router = useRoutes(RoutesStore.routes)return router } export default observer(MyRouter)
注意导入 router 对象时,用 observer 做了包装,这样能够在 store 发生变化时重建 router 对象