主页 > 开源代码  > 

前端基础学习


一、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>      <Search        placeholder='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.routeList      localStorage.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) {  // A8Login  const 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 对象

标签:

前端基础学习由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“前端基础学习