• jsp_servlet


     

    一、JSP基础概念

    1. 软件架构

    B/S架构:Browser/Server,浏览器-服务器

               最大的优点就是:一次部署,处处访问。

    C/S架构:Client/Server,客户端-服务器

               功能、事件丰富,可以完整的使用计算机的一系列资源。但是升级维护不方便。

    1. B/S架构工作原理

    采用请求+相应的工作模式进行交互。

    1.输入打开浏览器,输入网址,回车。

    2.请求将由客户端浏览器发送至WEB应用服务器,进行处理。

    3.WEB应用服务器将响应的结果(htmlcssjsimage...)发送回客户端浏览器

    4.浏览器渲染解析html,将页面呈现出来。

    1. URL

    定义:统一资源定位符。通过URL来寻找网络资源。

    格式:协议部分  主机地址  目标资源地址  参数

    示例:http://www.cnblogs.com/java/articlelist.jsp?key=tomcat

    解释:协议部分:http协议

    主机地址:www.cnblogs.com

    资源地址:java/articlelist.jsp

    参数部分:key=tomcat

        注意:http协议的默认端口是:80。

    1. web服务器

    是可以向发出请求的浏览器提供文档的程序。提供网上的信息浏览服务。

    常见的WEB服务器:IIS、Tomcat、WebLogic、Apache、Nginx......

    1. Tomcat

    下载地址:

    http://tomcat.apache.org/

    目录结构:

    1.bin目录:用于存放tomcat的一些核心组件、启动以及停止的命令。

            2.conf目录:TOMCAT的配置目录,里面存放了tomcat的一系列配置文件。

            3.lib目录:存放TOMCAT运行时需要的所有Jar包

            4.logs目录:存放tomcat启动、运行过程中产生的所有日志文件。

            5.temp目录:临时目录

            6.webapps目录:web网站发布的目录

            7.work目录:tomcat中发布的动态网站的工作目录。

    如何修改Tomcat的默认端口:

    1. 打开Tomcat目录,找到conf目录下的server.xml文件;
    2. 找到配置节<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
    3. 将port修改为目标端口。
    4. 在eclipse中配置tomcat

    1>.Window -> Pereferences 打开对话框

    2>.展开Server节点,选择Runtime Environment。

    3>.从右侧点击Add按钮,从对话框中选择Apache Tomcat 7.0,点击下一步

    4>.在弹出的Tomcat Server对话框中选择之前Tomcat的解压目录。点击完成即可。

    1. eclipse创建的动态网站目录结构介绍

    1>.java Resrouces -> src 目录:存放所有的java源文件

    2>.WebContent 目录:存放网站需要的jsp页面文件、html文件、css、js、image等资源。

    3>.build 目录:java文件、jsp文件的编译目录

    4>.Web Content -> WEB-INF 目录:该目录中的所有文件不会直接被客户端(浏览器)访问。

    所以一些敏感的资源文件可以放到本目录。比如网站的配置文件web.xml文件就存在于该目录。

    1. 在eclipse中如何发布web站点

    1>.打开Server窗口

    2>.右击Server窗口的空白区域,依次选择:New -> Server,弹出新建服务器窗口

    3>.从窗口中选择Tomcat v7.0 Server,点击Finishe即可。

    4>.双击新添加的Tomcat v7.0 Server节点,即可弹出新的对话框。

    从对话框中选择Server Locations,选中第二项:Use Tomcat installation;

    同时,Server Path修改为:webapps。

    最后Contrl + S保存。

    5>.右击Tomcat,选择“Add And Remove”

    从对话框的左侧选择目标网站,点击Add按钮即可添加到右侧。

    最后点击Finish。

    1. 解决JSP中文乱码的问题

    1>.Window -> Pereferences 打开对话框,选择General -> Content Types:

    从右侧选择Text -> JSP,然后在最下面的文本框中将ISO-8859-1改为UTF-8

    2>.选择General -> Workspace

    从右侧最下面,选择text file encoding,改为:UTF-8。

    3>.选择Web -> JSP Files节点

    从右侧Encoding选择ISO 10646/Unicode(UTF-8)

    最后点击OK即可。

    10. JSP

    JSP(Java  Server  Pages)。JSP是运行在服务器端的Java页面。使用HTML嵌套Java代码实现。

    11. 在JSP中编写JAVA代码

    1>.  <% ... %>

    在里面可以写多行java代码,每行用分号结尾。注意:不能定义方法。

    2>.  <%! ...  %>

    在里面只能定义全局变量和方法。

    3>.  <%= ... %>

    向页面输入内容。等价与<% out.write("abc"); %> == <%= "abc" %>

    12. 如果在jsp页面中导入其他的包

    在page指令中添加属性import,值为包名。

    <%@ page import="java.util.*" %>

    13. 配置默认页

    例如,将index.jsp配置成默认页。

    1>.打开web.xml文件,修改welcome-file-list;

    2>.在第一个节点中添加<welcome-file>index.jsp</welcome-file>

    注意:每次web.xml被修改以后,要重启Tomcat。

    14. get和post的区别

    1.get会将参数跟到url地址后面;post则不会。

    2.get提交的数据有大小限制,Post则不会。

    3.get提交不安全,post提交安全。

    4.get方式有利于Url传播,post则不利于url传播。

    15. Http状态码

    200    请求成功

    404    请求的资源不存在

    500    服务器内部错误

    302    重定向

    二、JSP九大内置对象

    1. 请求对象:request

    request.getRemoteAddr()   获取客户端的IP地址。

    request.setAttibute(String, Object)  存储数据

    Object request.getAttribute(String)  获取数据

    String 值 = request.getParameter("参数名");  获取参数的单个值

    String[] 值s = request.getParameterValues("参数名");获取参数的多个值

    注意:只有在同一次请求中存储的数据才能共享。

    1. 输出对象:out
    2. 响应对象:response

    转发和重定向

    重定向:客户端会先后发送2次请求到服务器。

    response.sendRedirect("welcome.jsp");

    重定向是客户端行为。

    转发:客户端向服务器发送一次请求,服务器端再请求另外一个页面地址,最后将结果响应给客户端

    request.getRequestDispatcher("login.jsp").forward(request, response);

    转发是服务器行为。

    1. 应用程序对象:application

    application是服务器开辟的一块共享数据存储区域。所有的会话均可以从application读写数据。

    application中存储的数据永远有效(服务器关闭时数据才会失效)。

    applicaton.setAttribute(String key, Object value);//存或修改数据

    Object applicaton.getAttribute(String key); //读取数据

    applicaton.removeAttribute(String key);//移除数据

    1. 会话对象:session

    客户端第一次向服务器发送请求以后,会话就会建立。当把浏览器关闭掉,重新再打开浏览器,就算是另一次新会话了。服务器会为每一个会话建立单独的数据存储区域。

    session中存储的数据有有效期的限制。session默认超时时间为30分钟。

    更改默认超时时间有两种方式:

    方式一:通过配置修改

    在web.xml文件中调整session失效时间:

    <session-config>

            <session-timeout>30</session-timeout>

    </session-config>

    方式二:通过Java代码修改

    session.setMaxInactiveInterval(30*60);//以秒为单位

    session其他方法介绍

    1.session.setAttribute(String key, Object value);//存或修改

    2.Object session.getAttribute(String key); //读取数据

    3.session.removeAttribute(String key);//移除数据

    4.session.invalid();//设置session失效(会话失效并且该会话所有存储的数据都将丢失)

    1. 页面上下文对象:pageContext
    2. 页面对象:page
    3. 配置对象:config
    4. 异常对象:exception

    三、Cookie介绍

    1>.cookie将数据存储客户端计算机。

    Cookie cookie1 = new Cookie(String, String);

    向客户端写cookie,使用response:response.addCookie(...);

    获取客户端的cookie,使用request:request.getCookies();

    2>.Cookie也有生命周期(有效期),通过cookie.setMaxAge(int seconds)。

    如果seconds=0,表示当关闭浏览器后,cookie自动失效;

    如果seconds<0,表示从客户端删除cookie;

    如果seconds>0,表示cookie失效的具体时间。

    注意:JSESSIONID是系统系统添加的一个cookie,每次会话创建后JSESSIONID就会添加到cookie中。

    3>.对于一些不敏感(不重要)的业务数据,并且数据量较小,就可以存储到cookie中。

    4>.cookie、session、application在存储数据方面的差异。

    相同点:

    cookie、session、application都可以存储数据。

    不同点:

    1.session和application都是把数据存储到服务器端;

    2.cookie将数据存储到客户端;

    3.session是用户会话级别,不同用户不能共享数据;application是全局的,所有用户共享数据。

    4.session和application会对服务器造成压力,而cookie不会。

    四、JSP乱码问题(java后台乱码)

    JSP默认采用ISO-8859-1编码,该编码不支持中文等字符。所以需要在后台设置编码格式。

    以GET方式发送请求时的编码解决方案:

    第一种:String title = request.getParameter("advtitle");

            String title1 = new String(title.getBytes("ISO-8859-1"), "UTF-8");

    第二种:在tomcat的conf->server.xml中修改Connector,增加URIEncoding属性。

    <Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

    以POST方式发送请求时的编码解决方案:

        request.setCharacterEncoding("UTF-8");

        response.setCharacterEncoding("UTF-8");

    五、JDBC

    1.基本概念

    定义:Java DataBase Connectivity。java数据库连接技术。

    用途:用于实现java程序和各种数据库(Oracle|Mysql|Sql Server...)的操作。

    JDBC提供了一组类和接口(API),它们位于java.sql以及javax.sql包中。

    2.JDBC访问数据库的步骤

    1>.加载驱动程序

    首先将驱动的jar包文件拷贝到web项目的lib目录中

    Class.for("驱动程序类");//加载驱动程序

    2>.获取连接对象

    连接字符串:jdbc:mysql://127.0.0.1:3306/newsdb?user=root&password=123456

    Connection connection = DriverManager.getConnection("连接字符串");

    3>.创建Statement,执行SQL语句

    Statement stmt = connection.createStatement();

    int count = Statement.executeUpate(String sql);

    注意:

    executeUpdate方法处理的SQL包括DML(insert|update|delete)和DDL(Create|ALTER|DROP)

            executeUpate()方法的返回值为int

    insert|update|delete返回值为大于0的值,表示成功

    create|alter|drop返回值永远为0

    Statement.executeQuery(String sql);

    注意:

    executeQuery方法处理的SQL包括DQL(select)

    4>.返回ResultSet结果或者数据库受影响的行数

    ResultSet set = stmt.executeQuery();

    while(set.next()==true){

        //循环一次取一条

        set.getString(arg0);//取字符串

        set.getInt(arg0);//取整数

        set.getDouble(columnLabel);//取double

    set.getFloat(columnIndex);//取float

        set.getDate(columnIndex);//取年月日

        set.getTime(columnIndex);//取时分秒

        set.getTimestamp(columnIndex)//取年月日时分秒    

    }

    5>.关闭连接

    connection.close();

    3. SQL注入

      用户输入一些特殊的字符,让程序中动态拼接的SQL语句,改变原来执行的含义,最终获取非预期的结果。

      导致SQL注入的原因就在于:程序中的SQL语句是通过字符串动态拼接的。

      例如:

    String sql = "select * from userinfo where username='"+uname+"' and password='"+upass+"'";

    如果uname = zhangsan     upass = 123456,那么上述sql变量的值为:

    select * from userinfo where username='zhangsan' and password='123456'

    如果uname = 1' or 1=1 or '1     upass = 123456,那么上述sql变量的值为:

    select * from userinfo where username='1' or 1=1 or '1' and password='123456'

    4.如何避免程序中被SQL注入?

    使用参数化查询。具体来说,就是在程序中尽量不要使用Statement类,而采用PreparedStatement类。

    Statement是PreparedStatement的父类。

    //SQL语句中,每一个参数使用?作为占位符

    String sql = "select count(*) from userinfo where username=? and userpass=?";

    PreparedStatement stmt = conn.prepareStatement(sql);

    stmt.setString(1, username);

    stmt.setString(2, userpass);

    ResultSet set = stmt.executeQuery();

    六、分层开发

    1.分层开发

      在项目实际开发过程中,会将整个项目从上到下划分为界面层、业务逻辑层、数据层。

      三层开发是项目开发实践中典型的开发模式。

      目的:实现高内聚、低耦合。

    2.各层功能

      界面层

        功能:负责数据展示,搜集用户输入。即和用户进行交互的层。

              界面层一般是指jsp页面、html页面等。

      业务逻辑层

        功能:负责处理功能模块的业务逻辑,以及界面层和数据层的数据流转。

        添加类的原则:

            根据功能模块来添加类。比如有用户管理模块,那么则可以添加UserManagerService类。

      数据层

        功能:和数据库进行交互。

              一般在数据层会编写SQL语句;JDBC;Hibernate、mybatis。

        添加类的原则:

            每张数据表均添加一个对应的数据操作类。比如用户表userinfo,则添加UserInfoDao类。

      实体层

        针对数据表创建出来的实体类;+ 用户自定义的一些实体类

    3.各个层之间的调用关系

      界面层  调用  业务逻辑层;

      业务逻辑层  调用  数据层; 

      数据层  操作  数据库;

      注意:调用的意思,就是在代码中创建对应的类对象,然后通过对象调用方法。

    4.分层开发的优缺点

      优点:

      1、开发人员可以只关注整个结构中的其中某一层;

      2、可以很容易的用新的实现来替换原有层次的实现;

      3、可以降低层与层之间的依赖;

      4、利于各层逻辑的复用。

      缺点:

      1、降低了系统的性能

      2、增加了程序的复杂度

      3、有时会导致级联的修改。

    七、Servlet

    1.Servlet基本概念

    每一个jsp页面就是一个java类,该类继承自:HttpJspBase -> HttpServlet。

    所以说一个jsp页面本质上就是一个servlet。

    Servlet的使用步骤

        1>.定义Servlet:

           添加一个普通的Java类,然后继承HttpServlet类。

           最后重写doGet和doPost方法。

           以get方式发送的请求进入doGet方法处理;以Post方式发送的请求进入doPost方法处理。

        2>.在web.xml中配置servlet。

    <servlet>

      <servlet-name>welcome_servlet</servlet-name>

      <servlet-class>jsp_charpter3_0908.Welcome</servlet-class>

    </servlet>

    <servlet-mapping>

      <servlet-name>welcome_servlet</servlet-name>

      <url-pattern>/welcome.html</url-pattern>

    </servlet-mapping>

           注意:每个servlet都要配置两个xml节点:servlet、servlet-mapping;

                 两个节点中servlet-name必须一样;

                 url-pattern:用于指定映射的url地址

    2.Content-Type

      内容类型,一般是指网页中存在的Content-Type,用于定义网络文件的类型和网页的编码。

    决定文件接收方将以什么形式、什么编码读取这个文件。

      常见的Content-Type

        text/css                层叠样式表

        text/javascript         js代码

        text/html               html代码

        text/plain              纯文本

        image/jpeg              图片jpg

        application/msword      word文档

        application/vnd.ms-excel     excel文档

        application/json

        response.setContentType("content-type");

    3.基于注解配置Servlet

      在添加的Servlet类上面添加注解:WebServlet

      name:设置servlet的名称

      urlPatterns:设置servlet的Url映射地址

      示例:

          @WebServlet(name="delete_goods", urlPatterns={"/goods/delete.html"})

          public class AddGoods extends HttpServlet{...}

    4.其他

      如何在Servlet中获取session

      HttpSession session = request.getSession();

    八、EL表达式

    1. 基本概念

    全名:Expression Language,即表达式语言。

    作用:它提供了在 JSP 中简化表达式的方法,让Jsp代码更加简化。代替JSP页面中复杂的JAVA代码。

    语法:EL表达式总是用大括号括起,而且前面有一个美元符($)前缀:${expression}。

    1. .[]与.运算符

    n  EL 提供.和[]两种运算符来存取数据。

    n  点号操作符允许使用一个Map键或一个bean属性名来访问值。

    例如:使用${foo.bar}可以得到bar属性的值。

    注意:放在点号操作符右边的东西必须遵循Java的标识符命名规则!

    n  []操作符比点号功能更强大,因为利用[]可以访问数组和List。

    例如,如果musicList是一个ArrayList,可以用${musicList[0]}或${musicList["0"]}来访问列表的第一个值。

    注意:当要存取的属性名称中包含一些特殊字符,如.或?等并非字母或数字的符号,就一定要使用 []。

    例如:${user.My-Name}应当改为${user["My-Name"] }

    如果要动态取值时,就可以用[]来做,而.无法做到动态取值。

    例如:${sessionScope.user[data]}中data 是一个变量。

    1. EL隐式(隐含)对象

    n  与范围有关的隐含对象

    pageScope、requestScope、sessionScope 和applicationScope。

    在EL中,这四个隐含对象只能用来取得范围属性值,即getAttribute(String name),却不能取得其他相关信息。例如:我们要取得session中储存一个属性username的值,java代码为:session.getAttribute("username")。而在EL中则使用下列方法:${sessionScope.username}。如果获取的对象为null,使用el表达式则不会输出null,但是通过getAttribute会在页面中输出Null。

    ${...Scope.name} 也可以这样写${name};

    此时el会依次从pageScope>requestScope>sessionScope>applicationScope中查找name。

    n  与输入有关的隐含对象

    与输入有关的隐含对象有两个:param和paramValues,它们是EL中比较特别的隐含对象。

    例如:我们要取得用户的请求参数时,可以利用下列方法:

    request.getParameter(String name)和request.getParameterValues(String name) 。

    在EL中则可以使用param和paramValues两者来取得数据。

    语法格式为:${param.name}和${paramValues.name}。

    1. 其他隐含对象

    1>.cookie

       例:要取得cookie中名称为userCountry的值,可以使用${cookie.userCountry}来取得它。

    2>.header和headerValues

       header 储存用户浏览器和服务端用来沟通的数据

       例:要取得用户浏览器的版本,可以使用${header["User-Agent"]}。

    1. EL运算符

    1>.算术运算符有五个:+、-、*或$、/或div、%或mod

    2>.关系运算符有六个:==或eq、!=或ne、<或lt、>或gt、<=或le、>=或ge

    3>.逻辑运算符有三个:&&或and、||或or、!或not

    4>.其它运算符有三个:Empty运算符、条件运算符、()运算符

       例:${empty param.name}、${A?B:C}、${A*(B+C)}

    1. 其他

    在JSP页面中可以动态的启用或者禁用EL表达式

         <%@ page isELIgnored="true"%> 表示是否禁用EL语言,TRUE表示禁止.FALSE表示不禁止.JSP2.0中默认的启用EL语言.

    九、JSTL

    1. JSTL简介

    JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。

    JSTL支持通用的、结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签,SQL标签。 除了这些,它还提供了一个框架来使用集成JSTL的自定义标签。

    根据JSTL标签所提供的功能,可以将其分为5个类别。

    n  核心标签

    n  格式化标签

    n  SQL 标签

    n  XML 标签

    n  JSTL 函数

    1. JSTL 库安装

    Apache Tomcat安装JSTL 库步骤如下:

    从Apache的标准标签库中下载的二进包(jakarta-taglibs-standard-current.zip)。

    下载地址:http://archive.apache.org/dist/jakarta/taglibs/standard/binaries/

    下载jakarta-taglibs-standard-1.1.1.zip 包并解压,将standard.jar和jstl.jar文件拷贝到/WEB-INF/lib/下。

    注意:使用任何库,必须在每个JSP文件中的头部包含<taglib>标签。

    1. 核心标签

    核心标签是最常用的JSTL标签。引用核心标签库的语法如下:

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

    标签                描述

    <c:out>            用于在JSP中显示数据,就像<%= ... >

    <c:set>            用于保存数据

    <c:remove>            用于删除数据

    <c:catch>         用来处理产生错误的异常状况,并且将错误信息储存起来

    <c:if>                 与我们在一般程序中用的if一样

    <c:choose>            本身只当做<c:when>和<c:otherwise>的父标签

    <c:when>           <c:choose>的子标签,用来判断条件是否成立

    <c:otherwise>         <c:choose>的子标签,接在<c:when>标签后,当<c:when>为false时被执行

    <c:import>           检索一个绝对或相对 URL,然后将其内容暴露给页面

    <c:forEach>      基础迭代标签,接受多种集合类型

    <c:forTokens>        根据指定的分隔符来分隔内容并迭代输出

    <c:param>        用来给包含或重定向的页面传递参数

    <c:redirect>    重定向至一个新的URL.

    <c:url>           使用可选的查询参数来创造一个URL

    forEach标签讲解:

    <c:forEach var="" items="" varStatus="" begin="" end="" step=""></c:forEach>

    【参数解析】:

             I、var设定变量名用于存储从集合中取出元素。

             II、items指定要遍历的集合。

             III、varStatus设定变量名,该变量用于存放集合中元素的信息。   

             IV、begin、end用于指定遍历的起始位置和终止位置(可选)。

             V、step指定循环的步长。

    【varStatus有4个状态属性】:

    index:当前循环的索引值   

    count:循环的次数  

    frist:是否为第一个位置  

    last:是否为最后一个位置

    1. 格式化标签

    JSTL格式化标签用来格式化并输出文本、日期、时间、数字。引用格式化标签库的语法如下:

    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

    标签                     描述

    <fmt:formatNumber>    使用指定的格式或精度格式化数字

    <fmt:formatDate>      使用指定的风格或模式格式化日期和时间

    <fmt:formatDate value="${xx}" pattern="dd/MM/yyyy hh:mm aa"/>

    十、过滤器

    1.过滤器

    概念:过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息。

        过滤器就是一个实现了Filter接口的Java类。

        在客户端的请求访问后端资源之前,拦截这些请求。

        在服务器的响应发送回客户端之前,处理这些响应。

    2.如何使用过滤器

    1>.定义一个类,实现接口Filter

    2>.重写init、destroy、doFilter方法。

    n  init(FilterConfig arg0)方法在初始化过滤器时被调用,在生命周期内只执行一次。

    n  doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)

    拦截请求的核心方法。

    //参数1:请求对象

    HttpServletRequest request = (HttpServletRequest)req;

    //参数2:响应对象

    HttpServletResponse response = (HttpServletResponse)resp;

    //参数3:过滤器链   chain

    n  destroy()方法,在过滤器被销毁时调用。

    3.课堂案例

    n  编码过滤器:解决post提交时,中文乱码问题

    n  权限过滤器:解决未登录状态下,拒绝访问系统资源。

    @WebFilter(filterName="login_filter", urlPatterns={"/*"})

    public class AuthFilter implements Filter {

     

    @Override

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)

             throws IOException, ServletException {

         //参数1:请求对象

         HttpServletRequest request = (HttpServletRequest)req;

         //参数2:响应对象

         HttpServletResponse response = (HttpServletResponse)resp;

            //排除登录

         String url = request.getRequestURL().toString();

         if(url.toLowerCase().contains("login.html")){

             //继续后续操作

             chain.doFilter(request, response);

             return;

         }

        

         //判断是否已经登录

         if(request.getSession().getAttribute("user")==null){

             //跳转到登录

             response.sendRedirect(request.getContextPath() + "/login.html");

         }else{

             //继续后续操作

             chain.doFilter(request, response);

         }

    }

     

    @Override

    public void destroy() {

    }

     

    @Override

    public void init(FilterConfig arg0) throws ServletException {

    }

    }

     

    n  获取请求消耗的时间的过滤器:查看系统中的性能瓶颈点。

    4.过滤器的应用场合:

    n  身份验证过滤器(Authentication Filters)。

    n  数据压缩过滤器(Data compression Filters)。

    n  加密过滤器(Encryption Filters)。

    n  触发资源访问事件过滤器。

    n  图像转换过滤器(Image Conversion Filters)。

    n  日志记录和审核过滤器(Logging and Auditing Filters)。

    n  MIME-TYPE 链过滤器(MIME-TYPE Chain Filters)。

    n  标记化过滤器(Tokenizing Filters)。

  • 相关阅读:
    IOS8定位
    ios通讯录基本操作2014-12月版
    ios悬浮按钮的实现
    MartinLiPageScrollView广告栏实现
    ios分享(友盟分享)
    vue2.0路由-适合刚接触新手简单理解
    git链接GitHub命令及基本操作
    Node
    JS数组sort()排序
    原生JS获取CSS样式并修改
  • 原文地址:https://www.cnblogs.com/liukuo/p/6425748.html
Copyright © 2020-2023  润新知