前端基础学习

一、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

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

(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<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>
}
  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<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 对象

相关推荐

  1. 前端学习|HTML基础笔记

    2024-03-20 19:54:01       21 阅读
  2. 前端学习笔记|CSS基础

    2024-03-20 19:54:01       21 阅读
  3. 前端学习----css基础语法

    2024-03-20 19:54:01       7 阅读
  4. 前端学习-CSS基础-Day1

    2024-03-20 19:54:01       15 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-20 19:54:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-20 19:54:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-20 19:54:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-20 19:54:01       20 阅读

热门阅读

  1. Spring Data访问Elasticsearch----查询方法

    2024-03-20 19:54:01       24 阅读
  2. 了解比特币分叉:演变与分歧

    2024-03-20 19:54:01       19 阅读
  3. Docker 命令大全

    2024-03-20 19:54:01       19 阅读
  4. LeetCode204. Count Primes

    2024-03-20 19:54:01       22 阅读
  5. 描述一下使用过的任何安全测试工具及其功能

    2024-03-20 19:54:01       24 阅读
  6. 分布式事务的实现方式

    2024-03-20 19:54:01       23 阅读
  7. C++ 函数模板

    2024-03-20 19:54:01       20 阅读