Web前端知识点个人小结

1、saas平台是什么?

SAAS软件即服务,平台是一种提供软件服务的模式,通过网络向客户提供软件应用程序。客户按照使用量支付相应的费用。SAAS平台适用于各种企业规模和行业,包括财务、人力资源、客户关系管理、项目管理等各种应用。相对于传统软件购买模式,SAAS平台降低了客户的软件使用成本,提高了软件的灵活性和可扩展性。

2、react中性能优化有哪些?

1. 避免不必要的渲染:对于依赖的数据没有发生变化的组件,可以通过shouldComponentUpdate或者React.memo避免不必要的渲染。

2. 利用PureComponent:PureComponent与普通组件不同的是,它会自动比较当前和前一个state和props的值,如果都相同,就不会重新渲染。

3. 使用不可变数据结构:由于React使用引用比较两个对象是否相等,当我们修改直接使用对象的属性值时,React有可能无法检测到变化。使用不可变数据结构可以确保每次修改生成新的对象,使得React可以正确检测到变化。

4. 使用React.PureComponent、React.memo、shouldComponentUpdate等方式对UI组件进行优化。

5. 数据优化,打破大型数据计算瓶颈,如缓存组件数据等。

6. 使用懒加载和代码拆分:对于长列表或者复杂页面,可以使用懒加载和代码拆分来优化性能。

7. 使用React的批量更新机制:React会自动将setState调用的批量更新到一起,避免了频繁的重新渲染。

8. 尽量减小虚拟DOM树的深度:虚拟DOM树越深,性能损耗就越大,在设计组件时需要尽量减小虚拟DOM树的深度。

9. 避免在render函数中使用箭头函数,因为箭头函数每次都会生成新的函数,会导致性能下降。

10. 使用Web Workers:Web Workers可以将耗时的任务放到另一个线程中执行,避免了主线程的阻塞和卡顿。

3、react中hooks性能优化有哪些?

1. 使用useCallback缓存函数组件,避免重复渲染。

2. 使用React.memo对组件进行浅层比较,只有属性有改变才会重新渲染。

3. 避免在组件渲染周期内进行重复的副作用操作,例如在useEffect中清除定时器和订阅。

4. 利用useRef缓存数据,避免每次渲染都重新计算。

5. 限制父组件的渲染,将子组件单独分离出来进行操作。    

6. 使用useLayoutEffect代替useEffect,在DOM更新之前进行必要的计算或者其他操作,以避免出现UI渲染闪烁。

7. 尽可能使用基于hooks的组件库,来减少开发者的手动处理。

4、react中hooks有哪些,及用法?

  • useState 是 React 中的高阶函数,它允许函数组件拥有本地状态。useState返回一个数组,包含当前状态值和一个更新该状态值的函数。

Eg:使用 useState 来管理一个计数器。

  • useEffect 允许在函数组件中执行副作用操作(如访问DOM或HTTP请求)。它类似于componentDidMountcomponentDidUpdatecomponentWillUnmount的组合。 useEffect接受两个参数,第一个参数是副作用函数,第二个参数是告诉React何时运行该函数。

Eg:组件装载时读取和清理定时器。

  • useContext 是 React 中的 Hooks 函数,用于在组件中共享数据,它接受一个上下文对象并返回其当前值。

Eg:Context Provider 和一个 Context Consumer组件传参。

  • useCallback 允许将内联回调函数返回一个记忆化版本以便减少子组件重新渲染的数量。

Eg:当传递给子组件的函数与父组件的状态或prop的变化时,可以使用useCallback来提高性能。

  • useMemo允许将一个函数的结果缓存起来,只有在依赖项改变时才重新计算结果。

Eg:它可用于性能优化,例如在渲染大型列表时避免不必要的计算。

5、vue3 中 新增及变化的内容?

1. `setup`: `setup()`函数是Vue3函数组件的入口点。它是唯一必须导出的函数,它返回一个包含模板中用到的数据、计算属性、方法和生命周期钩子的对象。`setup()`函数执行时会在组件实例创建前被调用。`setup()`函数中可以使用Vue 3中的所有其他Hooks。

2. `ref`: `ref()`函数用来创建一个响应式的数据引用。当ref数据发生变化时,组件会自动重新渲染。在模板中使用ref数据时需要访问其value属性。 调用`ref()`时,可以将任何数据作为参数传入,包括基本类型、对象、函数等。

3. `reactive`: `reactive()`函数用于创建一个响应式的对象。在模板中使用已响应式化对象的属性时不需要访问value。调用`reactive()`时,传入一个普通的JavaScript对象,返回一个可以被Vue3响应系统追踪的响应式对象。

4. `computed`: `computed()`函数用于创建一个计算属性。计算属性可以根据其他响应式变量的值自动更新自己的值,并且可以缓存计算结果以提高性能。

5. `watch`: `watch()`函数用于侦听一个响应式数据的变化,然后执行一个回调函数。`watch()`接收两个参数:要监视的数据和回调函数。回调函数接收新值和旧值。

6. `onMounted`: `onMounted()`函数在组件挂载到DOM后被调用,可以用来执行一些初始化任务。`onMounted()`接收一个回调函数作为参数。该回调函数在组件挂载到DOM后被调用。

7. `onUpdated`: `onUpdated()`函数会在组件更新后被调用。可以用来执行一些更新之后的任务。`onUpdated()`接收一个回调函数作为参数。该回调函数在组件更新后被调用。

8. `onUnmounted`: `onUnmounted()`函数会在组件从DOM移除前被调用。可以用来释放资源或清理事件监听器等。`onUnmounted()`接收一个回调函数作为参数。该回调函数在组件从DOM移除前被调用。

Hooks只能在组件的`setup()`函数中使用,不能在其他函数或回调中使用,只能在函数组件中使用,不能在基于Vue2的选项API组件中使用。

6、Nginx相关配置?

Nginx配置主要涉及以下方面:

1. 基础配置:包括全局配置、用户和进程设置、默认文件路径等。

2. 虚拟主机配置:可以设置多个虚拟主机,每个虚拟主机都有自己的配置。

3. 反向代理配置:允许将请求转发给其他服务器上的应用程序。

4. 负载均衡配置:允许分布式系统中多台服务器共同处理请求。

5. SSL/TLS配置:支持HTTPS协议和安全套接字层协议。

6. 缓存配置:允许保存部分数据以加快后续访问速度。

7. URL和HTTP协议的配置:包括限制访问和设置HTTP头部等。

Nginx 配置示例:

1. 基础配置示例:

```

worker_processes 4;

error_log /var/log/nginx/error.log;

pid /run/nginx.pid;

events {

    worker_connections 1024;

}

http {

    include /etc/nginx/mime.types;

    default_type application/octet-stream;

    access_log /var/log/nginx/access.log;

    sendfile on;

    keepalive_timeout 65;

    include /etc/nginx/conf.d/*.conf;

}

```

2. 虚拟主机配置示例:

```

server {

    listen 80 default_server;

    server_name _;

    root /var/www/html;

    index index.html;

    location / {

        try_files $uri $uri/ /index.html;

    }

}

server {

    listen 80;

    server_name example.com www.example.com;

    root /var/www/example;

    index index.html;

    location / {

        try_files $uri $uri/ /index.html;

    }

}

```

3. 反向代理配置示例:

```

server {

    listen 80;

    server_name example.com;

    location / {

        proxy_pass http://localhost:8080;

        proxy_set_header Host $host;

        proxy_set_header X-Real-IP $remote_addr;

    }

}

```

4. 负载均衡配置示例:

```

upstream app_servers {

    server 192.168.0.101:8080 weight=3;

    server 192.168.0.102:8080 weight=2;

    server 192.168.0.103:8080 weight=1;

}

server {

    listen 80;

    server_name example.com;

    location / {

        proxy_pass http://app_servers;

        proxy_set_header Host $host;

        proxy_set_header X-Real-IP $remote_addr;

    }

}

```

5. SSL/TLS配置示例:

```

server {

    listen 443 ssl http2;

    server_name example.com;

    ssl_certificate /etc/nginx/ssl/example.com.crt;

    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    ssl_session_cache shared:SSL:1m;

    ssl_session_timeout 10m;

    ssl_ciphers HIGH:!aNULL:!MD5;

    ssl_prefer_server_ciphers on;

    location / {

        proxy_pass http://app_servers;

        proxy_set_header Host $host;

        proxy_set_header X-Real-IP $remote_addr;

    }

}

```

6. 缓存配置示例:

```

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m inactive=60m;

proxy_cache_key "$scheme$request_method$host$request_uri";

proxy_cache_valid 200 60m;

proxy_cache_valid 404 1m;

server {

    listen 80;

    server_name example.com;

    location / {

        proxy_cache my_cache;

        proxy_pass http://app_servers;

        proxy_set_header Host $host;

        proxy_set_header X-Real-IP $remote_addr;

    }

}

```

7. URL和HTTP协议的配置示例:

```

server {

    listen 80;

    server_name example.com;

    location /secret {

        return 403;

    }

    location / {

        limit_except GET {

            deny all;

        }

        add_header X-Frame-Options SAMEORIGIN;

        add_header X-XSS-Protection "1; mode=block";

        add_header X-Content-Type-Options nosniff;

        proxy_pass http://app_servers;

        proxy_set_header Host $host;

        proxy_set_header X-Real-IP $remote_addr;

    }

}

```

7、手写promise,并且解释链式调用。

  handPromise() {

      // 请手写promise,并且解释链式调用。

      // 一个基本的 promise 实现如下:

      const PENDING = "pending";

      const FULFILLED = "fulfilled";

      const REJECTED = "rejected";

      function Promise(fn) {

        this.status = PENDING;

        this.value = undefined;

        this.reason = undefined;

        this.onFulfilledCallbacks = [];

        this.onRejectedCallbacks = [];

        const resolve = (value) => {

          if (this.status === PENDING) {

            this.status = FULFILLED;

            this.value = value;

            this.onFulfilledCallbacks.forEach((fn) => fn());

          }

        };

        const reject = (reason) => {

          if (this.status === PENDING) {

            this.status = REJECTED;

            this.reason = reason;

            this.onRejectedCallbacks.forEach((fn) => fn());

          }

        };

        try {

          fn(resolve, reject);

        } catch (error) {

          reject(error);

        }

      }

      Promise.prototype.then = function (onFulfilled, onRejected) {

        const realOnFulfilled =

          typeof onFulfilled === "function" ? onFulfilled : (v) => v;

        const realOnRejected =

          typeof onRejected === "function"

            ? onRejected

            : (r) => {

                throw r;

              };

        const promise2 = new Promise((resolve, reject) => {

          if (this.status === FULFILLED) {

            setTimeout(() => {

              try {

                const x = realOnFulfilled(this.value);

                resolvePromise(promise2, x, resolve, reject);

              } catch (error) {

                reject(error);

              }

            });

          } else if (this.status === REJECTED) {

            setTimeout(() => {

              try {

                const x = realOnRejected(this.reason);

                resolvePromise(promise2, x, resolve, reject);

              } catch (error) {

                reject(error);

              }

            });

          } else if (this.status === PENDING) {

            this.onFulfilledCallbacks.push(() => {

              setTimeout(() => {

                try {

                  const x = realOnFulfilled(this.value);

                  resolvePromise(promise2, x, resolve, reject);

                } catch (error) {

                  reject(error);

                }

              });

            });

            this.onRejectedCallbacks.push(() => {

              setTimeout(() => {

                try {

                  const x = realOnRejected(this.reason);

                  resolvePromise(promise2, x, resolve, reject);

                } catch (error) {

                  reject(error);

                }

              });

            });

          }

        });

        return promise2;

      };

      function resolvePromise(promise, x, resolve, reject) {

        if (promise === x) {

          return reject(new TypeError("Chaining cycle detected for promise"));

        }

        let called = false;

        if (x !== null && (typeof x === "object" || typeof x === "function")) {

          try {

            const then = x.then;

            if (typeof then === "function") {

              then.call(

                x,

                (y) => {

                  if (called) return;

                  called = true;

                  resolvePromise(promise, y, resolve, reject);

                },

                (r) => {

                  if (called) return;

                  called = true;

                  reject(r);

                }

              );

            } else {

              resolve(x);

            }

          } catch (error) {

            if (called) return;

            called = true;

            reject(error);

          }

        } else {

          resolve(x);

        }

      }

      // 链式调用指的是在 then 函数中返回一个 Promise 对象,使得可以对这个 Promise 对象继续调用 then 函数,从而实现一个连续的 Promise 调用链式语法。例如:

      //   promiseFn()

      //     .then(() => {

      //       console.log("第一次调用");

      //       return promiseFn();

      //     })

      //     .then(() => {

      //       console.log("第二次调用");

      //       return promiseFn();

      //     })

      //     .then(() => {

      //       console.log("第三次调用");

      //     });

      // 在上面的代码中,第一个 then 返回的是一个 Promise 对象,这个对象上调用了第二次 then,第二个 then 返回的又是一个 Promise 对象,这个对象上调用了第三次 then。这样的调用方式称之为链式调用。

    },

8、常见的前端微服务框架有哪些?

1. React Microfrontend:React是目前最受欢迎的前端框架之一。React Microfrontend则是一种将React应用程序组合到基于微服务的架构中的技术。

2. Angular Microfrontend:Angular是另一种受欢迎的前端框架,也支持微服务架构。Angular Microfrontend还提供了一些便利的功能,如共享代码和集成性。

3. Vue.js Microfrontend:Vue.js也是受欢迎的前端框架之一,与React和Angular一样,支持微服务架构。Vue.js还提供了与Vuex等多个工具的集成。

4. Webpack Module Federation:Webpack是一种可以将多个应用程序组合到一起的工具。Webpack Module Federation是一种新的技术,可用于在基于微服务的架构中使用Webpack。

5. Single-SPA:Single-SPA是一种流行的微前端框架,可帮助开发人员组合多个独立的应用程序,并确保它们一起协作。

6. Piral:Piral是一个基于React的微前端框架,具有丰富的插件系统,可提供许多与微服务有关的功能,例如动态加载和应用程序合并。

7. Qiankun:Qiankun是一个基于Single-SPA的微前端框架,可帮助开发人员构建多个独立的应用程序,并将它们组合到一个内置的应用程序中。

8. Luigi:Luigi是一种基于Microfrontend的框架,可以集成各种技术和应用程序,包括React、Angular、Vue.js和Single-SPA。它还提供了许多有用的功能,例如多语言支持,仪表盘内置等。

9、Jenkins你了解多少?

Jenkins是一款开源自动化持续集成部署工具。它允许用户在监视代码的版本控制系统中进行自动化构建、测试和部署。它是使用Java编写的,并提供了丰富的插件库,可以支持大多数现代软件开发流程。

Jenkins主要有以下几个特点:

1. 可扩展性:Jenkins提供了超过1000个插件,从而支持各种不同的工具和技术,满足不同开发团队的需求。

2. 易用性:Jenkins的用户界面简单友好,即使您不是开发人员也可以轻松上手。

3. 可定制性:Jenkins提供了广泛的配置选项,可以根据您的特定需求进行定制。

4. 高可靠性:Jenkins具有强大的错误处理机制,它可以自动检测并恢复错误。

Jenkins基本配置:

1. 安装Jenkins:在官网下载适合自己系统的Jenkins安装包,安装过程中按照提示进行即可。

2. 配置JDK:Jenkins需要JDK作为运行环境,需要在服务器上安装并配置好JDK。

3. 配置Jenkins:访问Jenkins的Web界面,进行基本配置,包括管理员密码、插件安装等。

4. 创建Jenkins Job:在Jenkins的Web界面上,点击“新建任务”按钮,选择类型,填写配置信息,保存即可。

5. 配置Jenkins Job:在Jenkins Job的配置界面上,填写代码仓库、构建器和选项等信息,配置完毕后保存。

6. 运行Jenkins Job:点击Jenkins Job界面上的“构建”按钮,启动构建流程并等待执行完成。

7. 查看Jenkins Job运行结果:Jenkins Job运行成功后,可以查看运行结果、日志和报告等信息。

10、Vue的组件传值方式有几种?

1. props:父组件通过props向子组件传递数据,子组件通过props接收父组件传递的数据。

2. $emit:子组件通过$emit触发一个自定义事件,父组件通过在子组件标签上绑定事件并在事件中响应得到子组件传递来的数据。

3. provide/inject:父组件通过provide向子组件提供数据,子组件通过inject注入父组件提供的数据。

4. ref:父组件通过$refs获取子组件实例,然后通过子组件实例直接访问或修改子组件内部的数据和方法。

在 Vue 中,可以通过 props 属性和 $emit 方法来实现组件之间的传值。

在使用 Vue 2.x 版本时,如果想在函数式组件中使用组件props,可以使用 inject 和 provide 来实现。

在使用 Vue 3.x 版本时,可以使用 setup 函数中的 props 和 emit 来实现,还提供了一个新的 API: useContext 和 useProvide,用于实现跨层级组件传递数据。

在 Hooks 中使用 ref 和 reactive 也可以实现组件之间的数据共享。例如:在父组件中使用 reactive 创建一个对象,然后通过 props 传递给子组件,在子组件中使用 toRefs 方法将对象转为响应式对象,然后使用 ref 方法获取其中的某个属性,并在模板中进行绑定。

11、React组件间传值的常用方式有几种?

1. Props传值:使用组件的属性(props)来传递数据或回调函数,这是最常用的一种方式。可以在父组件中通过给子组件传递不同的props来实现数据的传递。

2. Context传值:React组件树中的祖先组件可以使用React的Context机制将数据传递给后代组件。Context可以通过两个React API创建(React.createContext和Context.Provider)。

3. Ref传值:使用React.createRef方法创建ref对象,可以在组件中引用其他组件、DOM元素或JavaScript对象。

4. 状态提升:将多个组件共享的状态提升到他们的最近公共祖先组件中。这种方式会导致组件层次较深时,数据流比较复杂。

5. 全局状态管理工具:使用第三方状态管理库如Redux、Mobx等,来对全局状态进行集中管理,实现多组件间的数据传递。虽然依赖库多,但能够实现非常高效、可控的状态管理,在子组件中,使用 state 和 dispatch 函数来更新状态。

6.  useContext 钩子可用于传递数据到子组件中,而不必通过 props 属性来传递。它需要一个 createContext 来创建一个数据上下文,用于在整个应用程序中传递数据。

7. useReducer 钩子可以用于在两个组件之间传递状态。它需要一个 reducer 函数和一个初始状态作为参数,返回一个状态和一个 dispatch 函数,用于在组件中更新状态。

12、 JavaScript 中处理数组的方法有哪些?

1. push:向数组的末尾添加一个或多个元素,并返回新的长度。

2. pop:从数组的末尾移除一个元素,并返回该元素的值。

3. shift:从数组的开头移除一个元素,并返回该元素的值。

4. unshift:向数组的开头添加一个或多个元素,并返回新的长度。

5. splice:从数组中移除元素,并向数组中添加新元素。

6. slice:返回数组的一部分。

7. concat:连接两个或多个数组,并返回新的数组。

8. join:把数组中的所有元素转换为一个字符串。

9. reverse:反转数组的元素顺序。

10. sort:对数组的元素进行排序。

11. filter:返回满足条件的元素组成的新数组。

12. map:对数组的每个元素执行一次提供的函数,并返回一个新的数组。

13. reduce:对数组中的元素进行逐个操作,最终返回一个值。

14. forEach:对数组中的每个元素执行一次提供的函数。

15. indexOf:返回数组中第一个满足条件的元素的索引。

16. lastIndexOf:返回数组中最后一个满足条件的元素的索引。

17. every:测试数组中的所有元素是否满足条件。

18. some:测试数组中是否存在满足条件的元素。

19. fill:用指定的值填充数组中的全部或部分元素。

20. find:返回数组中满足条件的第一个元素。

21. findIndex:返回数组中满足条件的第一个元素的索引。

22. keys:返回一个包含数组中所有键的迭代器。

23. values:返回一个包含数组中所有值的迭代器。

24. entries:返回一个包含数组中所有键值对的迭代器。

25. flat:扁平化数组,使其变为一维数组。

26. flatMap:先执行map方法,再执行flat方法。

13、JavaScript 中处理对象的方法有哪些?

1. Object.getOwnPropertyNames(obj):返回一个字符串数组,列出对象自身所有属性的名称,包括不可枚举属性。

2. Object.keys(obj):返回一个字符串数组,列出对象自身可枚举属性的名称。

3. Object.values(obj):返回一个数组,列出对象自身可枚举属性的值。

4. Object.entries(obj):返回一个数组,列出对象自身可枚举属性的键值对,每个元素是一个数组,第一个元素是属性名,第二个元素是属性值。

5. Object.getOwnPropertyDescriptors(obj):返回一个对象,列出对象自身所有属性的描述符,包括不可枚举属性和Symbol属性。

6. Object.getOwnPropertyDescriptor(obj, prop):返回一个对象,描述指定属性的配置。

7. Object.create(proto[, propertiesObject]):创建新对象,将原型链指向proto,可选的propertiesObject用于定义新对象的属性描述符。

8. Object.setPrototypeOf(obj, proto):设置对象的原型链指向proto。

9. Object.getPrototypeOf(obj):返回对象的原型链上一层的对象。

10. Object.is(obj1, obj2):判断两个对象是否相同,包括+0和-0等特殊值。

11. Object.assign(target, ...sources):将多个源对象的属性复制到目标对象中,并返回目标对象。

12. Object.freeze(obj):冻结对象,禁止对象的属性被修改、新增或删除。

13. Object.isFrozen(obj):判断对象是否被冻结。

14. Object.seal(obj):封闭对象,禁止对象的属性被新增或删除,但是属性值可以修改。

15. Object.isSealed(obj):判断对象是否被封闭。

16. Object.isExtensible(obj):判断对象是否可扩展。

17. Object.preventExtensions(obj):禁止对象扩展,即不允许新属性被添加到对象中。

18. Object.defineProperty(obj, prop, descriptor):定义属性的描述符,包括属性的值、是否可枚举、是否可配置等。

19. Object.defineProperties(obj, descriptors):定义多个属性的描述符。

20. Object.getOwnPropertyDescriptor(obj, prop):获取指定属性的描述符。

14、什么是面向对象编程设计理念?

面向对象编程(Object-Oriented Programming,OOP)是一种程序设计范式,其中将现实世界中的实体定义为对象,这些对象拥有与它们相关的属性和方法。OOP 的设计理念包括以下几个方面:

1. 封装(Encapsulation):将数据和行为封装在对象内部,外部无法访问其内部实现细节,只能通过对象提供的方法来访问。

2. 继承(Inheritance):通过继承机制,子类可以继承父类的属性和方法,从而实现代码的复用和扩展。

3. 多态(Polymorphism):同一方法可以被不同的对象调用,从而实现不同的功能。这种灵活的特性可以通过继承、接口和重载等机制来实现。

4. 抽象(Abstraction):从多个具体的实体中抽象出共性,形成抽象类或接口,使得设计更加模块化和可维护。

面向对象编程的设计理念强调代码的模块化、灵活性和可扩展性,使得程序更加易于理解和维护。它也更加符合现实世界的描述方式,从而更加直观和易于沟通。

15、ChatGPT大模型如何理解?

ChatGPT是一个基于人工神经网络的自然语言处理模型,其全称是“Generative Pre-trained Transformer”,中文翻译为“预训练生成变压器”。该模型是由OpenAI团队开发的,旨在处理语言生成、问答和对话等任务。

ChatGPT模型背后的机制是一种名为Transformer的神经网络架构,它在处理自然语言时可以更易于捕捉语言的上下文和语义信息。ChatGPT的不同之处在于,在进行训练之前,OpenAI震撼性的使用了高达45TB的文本数据来训练该模型,这些文本数据包括互联网上的大量新闻、百科和社交媒体等来源。通过这种方式,ChatGPT可以更好地理解人类自然语言和对话,从而更好地回答问题,生成自然语言,甚至打造出更具人性化的机器人和虚拟助手。

16、Vue3.0跨组件传参插件了解吗?

"mitt": "^3.0.1",

man.ts中注入:

import mitt from 'mitt'

app.config.globalProperties.$mitt = mitt() // vue3 兄弟组件传参

页面中注入:

import { ComponentInternalInstance, getCurrentInstance, watchEffect} from 'vue'

const { appContext } = getCurrentInstance() as ComponentInternalInstance

// 传值的页面实时传递改变的值
const message = ref('AFeiNice')

watchEffect(() => {
  appContext.config.globalProperties.$mitt.emit('message', message)
  console.log(message.value) //记得打印
})

// 收值的页面获取实时改变的值
const msg1 = ref('')

watchEffect(() => {
  nextTick(() => {
    appContext.config.globalProperties.$mitt.on('message', (e: any) => {
        msg1.value = e.value
    })
    console.log(msg1.value) //记得打印
  })
})

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

袁广飞

感谢支持一位不知名的前端开发

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值