• 前端基础学习


    一、HTML、JS、CSS

    1、环境准备

    (1、)安装NVM

    (2、)检查NPM

    (3、)搭建简单服务

    二、框架

    1、VUE2

    (1、)环境准备

    1、)安装脚手架
    npm install -g @vue/cli
    • -g 参数表示全局安装,这样在任意目录都可以使用 vue 脚本创建项目

    2、)创建项目
    vue ui

    使用图形向导来创建 vue 项目,如下图,输入项目名

    选择手动配置项目

    添加 vue router 和 vuex

    选择版本,创建项目

    3、)安装 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

    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
      }
    })

    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
          }
        }
      }
    })

    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

    4、)浏览器插件

    插件地址 New React Developer Tools – React Blog (reactjs.org)

    VSCode

    推荐安装 Prettier 代码格式化插件

    三、组件库

    1、Element-UI

    2、Ant Design

    (1、)react 组件库

    (2、)安装

    npm install antd
    • 目前版本是 4.x

    引入样式,在 css 文件中加入

    @import '~antd/dist/antd.css';

    引入 antd 组件

    import { Button } from "antd";
    ​
    export default function A1() {
      return 
    }

    (3、)国际化

    试试其它组件

    import { Button, Modal } from "antd";
    ​
    export default function A1() {
      return 内容
    }

    发现确定和取消按钮是英文的,这是因为 antd 支持多种语言,而默认语言是英文

    要想改为中文,建议修改最外层的组件 index.tsx

    // ...
    import { ConfigProvider } from 'antd'
    import zhCN from 'antd/es/locale/zh_CN'

    root.render(
     
       
     

    )

    (4、)图标

    1、)图标要独立安装依赖

    npm install @ant-design/icons

    2、)图标组件,用来将字符串图标转换为标签图标

    import * as icons from '@ant-design/icons'
    ​
    interface Module {
      [p: string]: any
    }
    ​
    const all: Module = icons
    ​
    export default function Icon({ name }: { name: string }) {
      const Icon = all[name]
      return 
    }

    四、扩展

    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、)文档

    (2、)安装

    npm install mobx mobx-react-lite
    • mobx 目前版本是 6.x

    • mobx-react-lite 目前版本是 3.x

    (3、)名词

    Action, State, View

    • 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>(
          `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 (
        
          studentStore.fetch(Number(v))}        style={{ width: 100 }}      />      

    组件0 {studentStore.student.name}

                   
    ) } ​ export default observer(A7)

    其它组件

    import { observer } from 'mobx-react-lite'
    import studentStore from '../store/StudentStore'
    ​
    const A71 = () =>{
      return 

    {color:'red'}}>组件1 {studentStore.student.name}

    } ​ export default observer(A71)

    import { observer } from 'mobx-react-lite'
    import studentStore from '../store/StudentStore'
    ​
    const A72 = () =>{
      return 

    {color:'red'}}>组件1 {studentStore.student.name}

    } ​ 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>(`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
      }
    }

    六、请求

     1、Fetch-API

    2、Axios

    首先来学习 axios,作用是发送请求、接收响应,从服务器获取真实数据

    安装

    npm install axios

    定义组件

    import axios from 'axios'
    export default function P4({ id }: { id: number }) {
      async function updateStudent() {
        const resp = await axios.get(`http://localhost:8080/api/students/${id}`)
        console.log(resp.data.data)
      }
    ​
      updateStudent()
    ​
      return <>
    }
    • 其中 /api/students/${id} 是提前准备好的后端服务 api,会延迟 2s 返回结果

    使用组件

    在控制台上打印

    {
        "id": 1,
        "name": "宋远桥",
        "sex": "男",
        "age": 40
    }

    当属性变化时,会重新触发 P4 组件执行,例如将 id 从 1 修改为 2

    执行流程

    • 首次调用函数组件,返回的 jsx 代码会被渲染成【虚拟 dom 节点】(也称 Fiber 节点)

      • 根据【虚拟 dom 节点】会生成【真实 dom 节点】,由浏览器显示出来

    • 当函数组件的 props 或 state 发生变化时,才会重新调用函数组件,返回 jsx

      • jsx 与上次的【虚拟 dom 节点】对比

        • 如果没变化,复用上次的节点

        • 有变化,创建新的【虚拟 dom 节点】替换掉上次的节点

    • 由于严格模式会触发两次渲染,为了避免干扰,请先注释掉 index.tsx 中的

    七、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 
    }
    ​
    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:  },
    ]
    ​
    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(
      
        
          
        
        
    )

    A8Main 的代码

    import { Layout } from "antd";
    import { Link, Outlet } from "react-router-dom";
    ​
    export default function A8Main () {  
      return 
        头部导航
        
          侧边导航
            学生管理
            教师管理
            用户管理
          
          
            
          
        
      
    }
    1. Navigate 的作用是重定向

    2. load 方法的作用是懒加载组件,更重要的是根据字符串找到真正的组件,这是动态路由所需要的

    3. 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>(
          `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:  },
        ]
        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 
    }
    ​
    // 路由对象
    function MyRouter() {  
      const router = useRoutes(RoutesStore.routes)
      return router
    }
    ​
    export default observer(MyRouter)

    注意导入 router 对象时,用 observer 做了包装,这样能够在 store 发生变化时重建 router 对象

  • 相关阅读:
    【机器学习算法】神经网络与深度学习-7 DNN深度学习算法模型出现学习效果不好的情况,如何补救,对策如下,建议收藏。
    k8s+crio+podman搭建集群
    【App自动化测试】(六)移动端自动化中常用的元素定位方式
    华清 c++ day7 9月14
    全面吃透Stream流,让代码更优雅
    Nginx 配置 HTTPS 过程(+反向代理)
    Sql Server数据库附加数据库失败警告方法,有关详细信息,请单击“消息”列中的超链接(Win11)
    华为浏览器风险提示 - 解决方案
    【Css】Less和Sass的区别:
    1、密码学
  • 原文地址:https://blog.csdn.net/x2831582161/article/details/136833420